packages feed

JuicyPixels 3.3.5 → 3.3.9

raw patch · 17 files changed

Files

JuicyPixels.cabal view
@@ -1,5 +1,5 @@ Name:                JuicyPixels
-Version:             3.3.5
+Version:             3.3.9
 Synopsis:            Picture loading/serialization (in png, jpeg, bitmap, gif, tga, tiff and radiance)
 Description:
     <<data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADABAMAAACg8nE0AAAAElBMVEUAAABJqDSTWEL/qyb///8AAABH/1GTAAAAAXRSTlMAQObYZgAAAN5JREFUeF7s1sEJgFAQxFBbsAV72v5bEVYWPwT/XDxmCsi7zvHXavYREBDI3XP2GgICqBBYuwIC+/rVayPUAyAg0HvIXBcQoDFDGnUBgWQQ2Bx3AYFaRoBpAQHWb3bt2ARgGAiCYFFuwf3X5HA/McgGJWI2FdykCv4aBYzmKwDwvl6NVmUAAK2vlwEALK7fo88GANB6HQsAAAAAAAAA7P94AQCzswEAAAAAAAAAAAAAAAAAAICzh4UAO4zWAYBfRutHA4Bn5C69JhowAMGoBaMWDG0wCkbBKBgFo2AUAACPmegUST/IJAAAAABJRU5ErkJggg==>>
@@ -14,11 +14,21 @@ Category:            Codec, Graphics, Image
 Stability:           Stable
 Build-type:          Simple
-
--- Constraint on the version of Cabal needed to build this package.
-Cabal-version:       >= 1.18
+cabal-version: 1.18
+tested-with:
+  GHC == 9.8.1
+  GHC == 9.6.4
+  GHC == 9.4.8
+  GHC == 9.2.8
+  GHC == 9.0.2
+  GHC == 8.10.7
+  GHC == 8.8.4
+  GHC == 8.6.5
+  GHC == 8.4.4
+  GHC == 8.2.2
+  GHC == 8.0.2
 
-extra-source-files: changelog, docimages/*.png, docimages/*.svg, README.md
+extra-doc-files: changelog, docimages/*.png, docimages/*.svg, README.md
 extra-doc-files: docimages/*.png, docimages/*.svg
 
 Source-Repository head
@@ -28,7 +38,7 @@ Source-Repository this
     Type:      git
     Location:  git://github.com/Twinside/Juicy.Pixels.git
-    Tag:       v3.3.5
+    Tag:       v3.3.8
 
 Flag Mmap
     Description: Enable the file loading via mmap (memory map)
@@ -37,6 +47,7 @@ Library
   hs-source-dirs: src
   Default-Language: Haskell2010
+  default-extensions: TypeOperators
   Exposed-modules:  Codec.Picture,
                     Codec.Picture.Bitmap,
                     Codec.Picture.Gif,
@@ -66,16 +77,16 @@                     Codec.Picture.Tiff.Internal.Types
 
   Ghc-options: -O3 -Wall
-  Build-depends: base                >= 4.8     && < 6,
-                 bytestring          >= 0.9     && < 0.11,
-                 mtl                 >= 1.1     && < 2.3,
-                 binary              >= 0.8.1     && < 0.9,
-                 zlib                >= 0.5.3.1 && < 0.7,
+  Build-depends: base                >= 4.8     && < 5,
+                 bytestring          >= 0.9     && < 0.13,
+                 mtl                 >= 1.1     && < 2.4,
+                 binary              >= 0.8.1   && < 0.9,
+                 zlib                >= 0.5.3.1 && < 0.8,
                  transformers        >= 0.2,
-                 vector              >= 0.10    && < 0.13,
+                 vector              >= 0.12.3.1,
                  primitive           >= 0.4,
-                 deepseq             >= 1.1     && < 1.5,
-                 containers          >= 0.4.2   && < 0.7
+                 deepseq             >= 1.1     && < 1.6,
+                 containers          >= 0.4.2   && < 0.8
 
   -- Modules not exported by this package.
   Other-modules: Codec.Picture.BitWriter,
changelog view
@@ -1,6 +1,32 @@ Change log
 ==========
 
+v3.3.9 June 2024
+----------------
+
+ * Something something compilation
+
+v3.3.7 July 2022
+----------------
+
+ * Dependence fidling
+ * Jpg: do not call "error" in the parser, use fail instead.
+
+v3.3.7 March 2022
+-----------------
+
+ * Jpg: Fixing renderng bug with MCUs with single block in width and
+   multiple in height
+
+v3.3.6 October 2021
+-------------------
+
+ * Bytestring bound bump
+ * Fix bug #187.  (Some JPEGs are misidentified as SourceTiff.)
+ * Fix EXIF handling of strings of four characters or fewer.
+ * Fix endianness bug in short ExifString and ExifUndefined.
+
+
 v3.3.5 January 2020
 -------------------
 
docimages/pixelgraph.svg view
@@ -1,264 +1,264 @@-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
- "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Generated by graphviz version 2.22.2 (20090313.1817)
- -->
-<!-- Title: pixelGraphic Pages: 1 -->
-<svg width="620pt" height="464pt"
- viewBox="0.00 0.00 620.00 464.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 460)">
-<title>pixelGraphic</title>
-<polygon fill="white" stroke="white" points="-4,5 -4,-460 617,-460 617,5 -4,5"/>
-<!-- PixelRGB8 -->
-<g id="node1" class="node"><title>PixelRGB8</title>
-<ellipse fill="none" stroke="black" cx="246" cy="-438" rx="31.7878" ry="18"/>
-<text text-anchor="middle" x="246" y="-436.3" font-family="sans-serif" font-size="8.00">PixelRGB8</text>
-</g>
-<!-- PixelRGBA8 -->
-<g id="node6" class="node"><title>PixelRGBA8</title>
-<ellipse fill="none" stroke="black" cx="441" cy="-354" rx="34.8223" ry="18"/>
-<text text-anchor="middle" x="441" y="-352.3" font-family="sans-serif" font-size="8.00">PixelRGBA8</text>
-</g>
-<!-- PixelRGB8&#45;&gt;PixelRGBA8 -->
-<g id="edge4" class="edge"><title>PixelRGB8&#45;&gt;PixelRGBA8</title>
-<path fill="none" stroke="black" d="M274.25,-429.506C295.421,-422.833 324.934,-412.874 350,-402 351.523,-401.339 381.994,-385.244 407.261,-371.871"/>
-<polygon fill="black" stroke="black" points="409.071,-374.874 416.272,-367.102 405.796,-368.687 409.071,-374.874"/>
-<text text-anchor="middle" x="393" y="-394.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- PixelRGBF -->
-<g id="node8" class="node"><title>PixelRGBF</title>
-<ellipse fill="none" stroke="black" cx="33" cy="-102" rx="31.7878" ry="18"/>
-<text text-anchor="middle" x="33" y="-100.3" font-family="sans-serif" font-size="8.00">PixelRGBF</text>
-</g>
-<!-- PixelRGB8&#45;&gt;PixelRGBF -->
-<g id="edge6" class="edge"><title>PixelRGB8&#45;&gt;PixelRGBF</title>
-<path fill="none" stroke="black" d="M214.594,-434.005C148.276,-424.689 0,-398.847 0,-354 0,-354 0,-354 0,-186 0,-165.437 8.67323,-143.88 17.1268,-127.753"/>
-<polygon fill="black" stroke="black" points="20.228,-129.378 22.0115,-118.934 14.1045,-125.986 20.228,-129.378"/>
-<text text-anchor="middle" x="23" y="-268.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel8 -->
-<g id="node20" class="node"><title>Pixel8</title>
-<ellipse fill="none" stroke="black" cx="308" cy="-270" rx="27" ry="18"/>
-<text text-anchor="middle" x="308" y="-268.3" font-family="sans-serif" font-size="8.00">Pixel8</text>
-</g>
-<!-- PixelRGB8&#45;&gt;Pixel8 -->
-<g id="edge60" class="edge"><title>PixelRGB8&#45;&gt;Pixel8</title>
-<path fill="none" stroke="black" d="M214.516,-433.811C149.341,-423.852 9.37863,-395.049 55,-336 81.2893,-301.973 207.23,-282.163 270.929,-274.163"/>
-<polygon fill="black" stroke="black" points="271.46,-277.624 280.961,-272.936 270.61,-270.676 271.46,-277.624"/>
-<text text-anchor="middle" x="80" y="-352.3" font-family="sans-serif" font-size="8.00">computeLuma</text>
-</g>
-<!-- PixelCMYK8 -->
-<g id="node28" class="node"><title>PixelCMYK8</title>
-<ellipse fill="none" stroke="black" cx="253" cy="-354" rx="34.8223" ry="18"/>
-<text text-anchor="middle" x="253" y="-352.3" font-family="sans-serif" font-size="8.00">PixelCMYK8</text>
-</g>
-<!-- PixelRGB8&#45;&gt;PixelCMYK8 -->
-<g id="edge40" class="edge"><title>PixelRGB8&#45;&gt;PixelCMYK8</title>
-<path fill="none" stroke="black" d="M224.748,-423.894C213.935,-414.831 204.182,-402.459 210,-390 212.758,-384.095 217.013,-378.817 221.794,-374.241"/>
-<polygon fill="black" stroke="black" points="224.225,-376.77 229.601,-367.64 219.705,-371.424 224.225,-376.77"/>
-<text text-anchor="middle" x="232" y="-394.3" font-family="sans-serif" font-size="8.00">convertPixel</text>
-</g>
-<!-- PixelYCbCr8 -->
-<g id="node30" class="node"><title>PixelYCbCr8</title>
-<ellipse fill="none" stroke="black" cx="150" cy="-354" rx="35.9084" ry="18"/>
-<text text-anchor="middle" x="150" y="-352.3" font-family="sans-serif" font-size="8.00">PixelYCbCr8</text>
-</g>
-<!-- PixelRGB8&#45;&gt;PixelYCbCr8 -->
-<g id="edge42" class="edge"><title>PixelRGB8&#45;&gt;PixelYCbCr8</title>
-<path fill="none" stroke="black" d="M214.399,-434.415C177.053,-429.451 118.29,-419.109 105,-402 97.106,-391.837 104.874,-381.324 116.049,-372.736"/>
-<polygon fill="black" stroke="black" points="118.35,-375.401 124.612,-366.856 114.387,-369.631 118.35,-375.401"/>
-<text text-anchor="middle" x="127" y="-394.3" font-family="sans-serif" font-size="8.00">convertPixel</text>
-</g>
-<!-- PixelRGB16 -->
-<g id="node2" class="node"><title>PixelRGB16</title>
-<ellipse fill="none" stroke="black" cx="429" cy="-102" rx="34.1425" ry="18"/>
-<text text-anchor="middle" x="429" y="-100.3" font-family="sans-serif" font-size="8.00">PixelRGB16</text>
-</g>
-<!-- PixelRGBA16 -->
-<g id="node4" class="node"><title>PixelRGBA16</title>
-<ellipse fill="none" stroke="black" cx="343" cy="-18" rx="37.1721" ry="18"/>
-<text text-anchor="middle" x="343" y="-16.3" font-family="sans-serif" font-size="8.00">PixelRGBA16</text>
-</g>
-<!-- PixelRGB16&#45;&gt;PixelRGBA16 -->
-<g id="edge2" class="edge"><title>PixelRGB16&#45;&gt;PixelRGBA16</title>
-<path fill="none" stroke="black" d="M400.098,-92.2134C386.67,-86.4097 371.409,-77.8363 361,-66 355.909,-60.2106 352.202,-52.8433 349.526,-45.6287"/>
-<polygon fill="black" stroke="black" points="352.846,-44.5187 346.482,-36.0479 346.174,-46.6383 352.846,-44.5187"/>
-<text text-anchor="middle" x="384" y="-58.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel16 -->
-<g id="node16" class="node"><title>Pixel16</title>
-<ellipse fill="none" stroke="black" cx="296" cy="-186" rx="27" ry="18"/>
-<text text-anchor="middle" x="296" y="-184.3" font-family="sans-serif" font-size="8.00">Pixel16</text>
-</g>
-<!-- PixelRGB16&#45;&gt;Pixel16 -->
-<g id="edge58" class="edge"><title>PixelRGB16&#45;&gt;Pixel16</title>
-<path fill="none" stroke="black" d="M424.573,-120.149C421.265,-130.199 415.765,-142.199 407,-150 386.436,-168.304 356.427,-177.32 332.795,-181.75"/>
-<polygon fill="black" stroke="black" points="332.056,-178.325 322.773,-183.431 333.214,-185.228 332.056,-178.325"/>
-<text text-anchor="middle" x="441" y="-142.3" font-family="sans-serif" font-size="8.00">computeLuma</text>
-</g>
-<!-- PixelCMYK16 -->
-<g id="node26" class="node"><title>PixelCMYK16</title>
-<ellipse fill="none" stroke="black" cx="553" cy="-18" rx="37.1721" ry="18"/>
-<text text-anchor="middle" x="553" y="-16.3" font-family="sans-serif" font-size="8.00">PixelCMYK16</text>
-</g>
-<!-- PixelRGB16&#45;&gt;PixelCMYK16 -->
-<g id="edge38" class="edge"><title>PixelRGB16&#45;&gt;PixelCMYK16</title>
-<path fill="none" stroke="black" d="M451.832,-88.3606C462.615,-81.7841 475.593,-73.6731 487,-66 499.46,-57.6189 512.915,-47.9503 524.406,-39.4968"/>
-<polygon fill="black" stroke="black" points="526.807,-42.074 532.761,-33.3106 522.641,-36.4482 526.807,-42.074"/>
-<text text-anchor="middle" x="525" y="-58.3" font-family="sans-serif" font-size="8.00">convertPixel</text>
-</g>
-<!-- PixelRGBA16&#45;&gt;PixelRGB16 -->
-<g id="edge44" class="edge"><title>PixelRGBA16&#45;&gt;PixelRGB16</title>
-<path fill="none" stroke="black" d="M371.789,-29.3863C383.871,-35.2918 397.329,-43.4955 407,-54 412.378,-59.8422 416.636,-67.2266 419.915,-74.4406"/>
-<polygon fill="black" stroke="black" points="416.794,-76.0584 423.794,-84.0115 423.282,-73.4291 416.794,-76.0584"/>
-<text text-anchor="middle" x="449" y="-58.3" font-family="sans-serif" font-size="8.00">dropTransparency</text>
-</g>
-<!-- PixelRGBA8&#45;&gt;PixelRGB8 -->
-<g id="edge46" class="edge"><title>PixelRGBA8&#45;&gt;PixelRGB8</title>
-<path fill="none" stroke="black" d="M437.029,-372.038C433.845,-382.309 428.318,-394.571 419,-402 399.083,-417.88 332.208,-428.306 287.678,-433.672"/>
-<polygon fill="black" stroke="black" points="287.239,-430.2 277.712,-434.834 288.05,-437.153 287.239,-430.2"/>
-<text text-anchor="middle" x="463" y="-394.3" font-family="sans-serif" font-size="8.00">dropTransparency</text>
-</g>
-<!-- PixelRGBA8&#45;&gt;Pixel8 -->
-<g id="edge52" class="edge"><title>PixelRGBA8&#45;&gt;Pixel8</title>
-<path fill="none" stroke="black" d="M413.829,-342.599C399.476,-336.131 381.815,-327.451 367,-318 354.878,-310.267 342.394,-300.428 332.048,-291.673"/>
-<polygon fill="black" stroke="black" points="334.114,-288.833 324.258,-284.946 329.539,-294.131 334.114,-288.833"/>
-<text text-anchor="middle" x="392" y="-310.3" font-family="sans-serif" font-size="8.00">computeLuma</text>
-</g>
-<!-- PixelF -->
-<g id="node14" class="node"><title>PixelF</title>
-<ellipse fill="none" stroke="black" cx="106" cy="-186" rx="27" ry="18"/>
-<text text-anchor="middle" x="106" y="-184.3" font-family="sans-serif" font-size="8.00">PixelF</text>
-</g>
-<!-- PixelRGBF&#45;&gt;PixelF -->
-<g id="edge56" class="edge"><title>PixelRGBF&#45;&gt;PixelF</title>
-<path fill="none" stroke="black" d="M27.0137,-120.147C24.9943,-129.716 24.4635,-141.251 30,-150 38.9021,-164.067 54.9514,-172.745 70.015,-178.044"/>
-<polygon fill="black" stroke="black" points="69.0981,-181.424 79.6867,-181.06 71.1825,-174.741 69.0981,-181.424"/>
-<text text-anchor="middle" x="55" y="-142.3" font-family="sans-serif" font-size="8.00">computeLuma</text>
-</g>
-<!-- PixelYA16 -->
-<g id="node9" class="node"><title>PixelYA16</title>
-<ellipse fill="none" stroke="black" cx="259" cy="-102" rx="31.7878" ry="18"/>
-<text text-anchor="middle" x="259" y="-100.3" font-family="sans-serif" font-size="8.00">PixelYA16</text>
-</g>
-<!-- PixelYA16&#45;&gt;PixelRGBA16 -->
-<g id="edge8" class="edge"><title>PixelYA16&#45;&gt;PixelRGBA16</title>
-<path fill="none" stroke="black" d="M258.152,-83.5434C258.613,-73.883 260.687,-62.36 267,-54 275.273,-43.0444 287.722,-35.28 300.163,-29.8405"/>
-<polygon fill="black" stroke="black" points="301.586,-33.0421 309.607,-26.1198 299.02,-26.5294 301.586,-33.0421"/>
-<text text-anchor="middle" x="290" y="-58.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- PixelYA16&#45;&gt;Pixel16 -->
-<g id="edge48" class="edge"><title>PixelYA16&#45;&gt;Pixel16</title>
-<path fill="none" stroke="black" d="M229.98,-109.854C206.015,-117.845 177.897,-131.656 191,-150 199.293,-161.611 233.125,-171.835 260.175,-178.391"/>
-<polygon fill="black" stroke="black" points="259.609,-181.853 270.143,-180.72 261.201,-175.037 259.609,-181.853"/>
-<text text-anchor="middle" x="225" y="-142.3" font-family="sans-serif" font-size="8.00">dropTransparency</text>
-</g>
-<!-- PixelYA8 -->
-<g id="node11" class="node"><title>PixelYA8</title>
-<ellipse fill="none" stroke="black" cx="439" cy="-186" rx="28.8445" ry="18"/>
-<text text-anchor="middle" x="439" y="-184.3" font-family="sans-serif" font-size="8.00">PixelYA8</text>
-</g>
-<!-- PixelYA8&#45;&gt;PixelRGB8 -->
-<g id="edge12" class="edge"><title>PixelYA8&#45;&gt;PixelRGB8</title>
-<path fill="none" stroke="black" d="M464.762,-194.278C489.627,-203.81 525.815,-222.121 542,-252 574.918,-312.769 554.613,-358.387 501,-402 484.752,-415.217 355.671,-428.373 287.639,-434.476"/>
-<polygon fill="black" stroke="black" points="287.321,-430.99 277.669,-435.359 287.938,-437.963 287.321,-430.99"/>
-<text text-anchor="middle" x="580" y="-310.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- PixelYA8&#45;&gt;PixelRGBA8 -->
-<g id="edge10" class="edge"><title>PixelYA8&#45;&gt;PixelRGBA8</title>
-<path fill="none" stroke="black" d="M460.035,-198.84C467.824,-204.817 475.781,-212.68 480,-222 497.598,-260.871 496.093,-278.904 479,-318 476.725,-323.204 473.295,-328.013 469.411,-332.309"/>
-<polygon fill="black" stroke="black" points="466.792,-329.975 462.079,-339.464 471.681,-334.985 466.792,-329.975"/>
-<text text-anchor="middle" x="515" y="-268.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- PixelYA8&#45;&gt;Pixel8 -->
-<g id="edge50" class="edge"><title>PixelYA8&#45;&gt;Pixel8</title>
-<path fill="none" stroke="black" d="M412.102,-193.018C393.267,-198.789 368.145,-208.269 349,-222 340.05,-228.419 331.808,-237.196 325.098,-245.49"/>
-<polygon fill="black" stroke="black" points="322.247,-243.456 318.929,-253.518 327.797,-247.721 322.247,-243.456"/>
-<text text-anchor="middle" x="383" y="-226.3" font-family="sans-serif" font-size="8.00">dropTransparency</text>
-</g>
-<!-- PixelYA8&#45;&gt;Pixel8 -->
-<g id="edge62" class="edge"><title>PixelYA8&#45;&gt;Pixel8</title>
-<path fill="none" stroke="black" d="M434.558,-204.131C431.244,-214.175 425.744,-226.175 417,-234 397.039,-251.864 367.99,-260.893 344.899,-265.441"/>
-<polygon fill="black" stroke="black" points="344.002,-262.045 334.763,-267.23 345.219,-268.938 344.002,-262.045"/>
-<text text-anchor="middle" x="451" y="-226.3" font-family="sans-serif" font-size="8.00">computeLuma</text>
-</g>
-<!-- PixelF&#45;&gt;PixelRGBF -->
-<g id="edge14" class="edge"><title>PixelF&#45;&gt;PixelRGBF</title>
-<path fill="none" stroke="black" d="M100.45,-168.312C96.8232,-158.653 91.3784,-146.886 84,-138 78.5966,-131.492 71.7476,-125.606 64.8367,-120.568"/>
-<polygon fill="black" stroke="black" points="66.3898,-117.392 56.1539,-114.657 62.4504,-123.178 66.3898,-117.392"/>
-<text text-anchor="middle" x="115" y="-142.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel16&#45;&gt;PixelRGB16 -->
-<g id="edge18" class="edge"><title>Pixel16&#45;&gt;PixelRGB16</title>
-<path fill="none" stroke="black" d="M313.43,-172.019C326.239,-162.023 344.257,-148.532 361,-138 371.689,-131.277 383.756,-124.61 394.725,-118.875"/>
-<polygon fill="black" stroke="black" points="396.336,-121.982 403.628,-114.295 393.134,-115.757 396.336,-121.982"/>
-<text text-anchor="middle" x="384" y="-142.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel16&#45;&gt;PixelRGBA16 -->
-<g id="edge16" class="edge"><title>Pixel16&#45;&gt;PixelRGBA16</title>
-<path fill="none" stroke="black" d="M305.084,-168.741C307.903,-162.903 310.835,-156.272 313,-150 325.048,-115.091 333.609,-73.3023 338.424,-46.2333"/>
-<polygon fill="black" stroke="black" points="341.89,-46.732 340.143,-36.2822 334.992,-45.5405 341.89,-46.732"/>
-<text text-anchor="middle" x="353" y="-100.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel16&#45;&gt;PixelYA16 -->
-<g id="edge20" class="edge"><title>Pixel16&#45;&gt;PixelYA16</title>
-<path fill="none" stroke="black" d="M280.78,-170.7C275.635,-164.701 270.35,-157.469 267,-150 264.277,-143.93 262.473,-137.052 261.279,-130.438"/>
-<polygon fill="black" stroke="black" points="264.727,-129.827 259.862,-120.415 257.796,-130.807 264.727,-129.827"/>
-<text text-anchor="middle" x="290" y="-142.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel8&#45;&gt;PixelRGB8 -->
-<g id="edge24" class="edge"><title>Pixel8&#45;&gt;PixelRGB8</title>
-<path fill="none" stroke="black" d="M312.919,-288.095C319.136,-314.953 326.712,-366.057 306,-402 300.622,-411.332 291.773,-418.518 282.501,-423.926"/>
-<polygon fill="black" stroke="black" points="280.83,-420.85 273.581,-428.577 284.067,-427.057 280.83,-420.85"/>
-<text text-anchor="middle" x="342" y="-352.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel8&#45;&gt;PixelRGBA8 -->
-<g id="edge22" class="edge"><title>Pixel8&#45;&gt;PixelRGBA8</title>
-<path fill="none" stroke="black" d="M334.939,-272.185C360.073,-275.561 397.177,-284.15 421,-306 426.855,-311.37 431.048,-318.72 434.03,-326.054"/>
-<polygon fill="black" stroke="black" points="430.836,-327.531 437.387,-335.858 437.458,-325.263 430.836,-327.531"/>
-<text text-anchor="middle" x="452" y="-310.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel8&#45;&gt;PixelYA8 -->
-<g id="edge26" class="edge"><title>Pixel8&#45;&gt;PixelYA8</title>
-<path fill="none" stroke="black" d="M290.77,-255.865C280.734,-245.919 271.57,-232.553 280,-222 287.559,-212.538 356.092,-199.635 400.682,-192.122"/>
-<polygon fill="black" stroke="black" points="401.335,-195.562 410.626,-190.469 400.187,-188.657 401.335,-195.562"/>
-<text text-anchor="middle" x="303" y="-226.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel8&#45;&gt;PixelF -->
-<g id="edge28" class="edge"><title>Pixel8&#45;&gt;PixelF</title>
-<path fill="none" stroke="black" d="M281.145,-267.812C249.741,-264.331 196.969,-255.554 157,-234 145.577,-227.84 134.814,-218.473 126.146,-209.643"/>
-<polygon fill="black" stroke="black" points="128.492,-207.028 119.104,-202.117 123.381,-211.81 128.492,-207.028"/>
-<text text-anchor="middle" x="180" y="-226.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- Pixel8&#45;&gt;Pixel16 -->
-<g id="edge30" class="edge"><title>Pixel8&#45;&gt;Pixel16</title>
-<path fill="none" stroke="black" d="M282.206,-263.957C256.398,-257.327 219.359,-246.018 211,-234 196.025,-212.469 230.216,-199.429 259.604,-192.482"/>
-<polygon fill="black" stroke="black" points="260.687,-195.828 269.71,-190.275 259.193,-188.989 260.687,-195.828"/>
-<text text-anchor="middle" x="234" y="-226.3" font-family="sans-serif" font-size="8.00">promotePixel</text>
-</g>
-<!-- PixelCMYK16&#45;&gt;PixelRGB16 -->
-<g id="edge32" class="edge"><title>PixelCMYK16&#45;&gt;PixelRGB16</title>
-<path fill="none" stroke="black" d="M564.03,-35.4654C568.707,-45.2873 571.707,-57.2873 565,-66 553.678,-80.7091 507.858,-90.6423 472.496,-96.2857"/>
-<polygon fill="black" stroke="black" points="471.73,-92.862 462.373,-97.8319 472.787,-99.7817 471.73,-92.862"/>
-<text text-anchor="middle" x="590" y="-58.3" font-family="sans-serif" font-size="8.00">convertPixel</text>
-</g>
-<!-- PixelCMYK8&#45;&gt;PixelRGB8 -->
-<g id="edge34" class="edge"><title>PixelCMYK8&#45;&gt;PixelRGB8</title>
-<path fill="none" stroke="black" d="M257.173,-372.299C258.645,-381.28 259.584,-392.241 258,-402 257.535,-404.865 256.875,-407.811 256.1,-410.724"/>
-<polygon fill="black" stroke="black" points="252.737,-409.753 253.143,-420.34 259.427,-411.811 252.737,-409.753"/>
-<text text-anchor="middle" x="280" y="-394.3" font-family="sans-serif" font-size="8.00">convertPixel</text>
-</g>
-<!-- PixelYCbCr8&#45;&gt;PixelRGB8 -->
-<g id="edge36" class="edge"><title>PixelYCbCr8&#45;&gt;PixelRGB8</title>
-<path fill="none" stroke="black" d="M149.836,-372.162C150.645,-381.97 153.174,-393.727 160,-402 171.431,-415.854 189.112,-424.462 205.475,-429.768"/>
-<polygon fill="black" stroke="black" points="204.847,-433.23 215.426,-432.663 206.802,-426.509 204.847,-433.23"/>
-<text text-anchor="middle" x="182" y="-394.3" font-family="sans-serif" font-size="8.00">convertPixel</text>
-</g>
-<!-- PixelYCbCr8&#45;&gt;Pixel8 -->
-<g id="edge54" class="edge"><title>PixelYCbCr8&#45;&gt;Pixel8</title>
-<path fill="none" stroke="black" d="M174.688,-340.875C202.411,-326.136 247.432,-302.201 277.376,-286.281"/>
-<polygon fill="black" stroke="black" points="279.34,-289.201 286.527,-281.416 276.054,-283.02 279.34,-289.201"/>
-<text text-anchor="middle" x="263" y="-310.3" font-family="sans-serif" font-size="8.00">computeLuma</text>
-</g>
-</g>
-</svg>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">+<!-- Generated by graphviz version 2.22.2 (20090313.1817)+ -->+<!-- Title: pixelGraphic Pages: 1 -->+<svg width="620pt" height="464pt"+ viewBox="0.00 0.00 620.00 464.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">+<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 460)">+<title>pixelGraphic</title>+<polygon fill="white" stroke="white" points="-4,5 -4,-460 617,-460 617,5 -4,5"/>+<!-- PixelRGB8 -->+<g id="node1" class="node"><title>PixelRGB8</title>+<ellipse fill="none" stroke="black" cx="246" cy="-438" rx="31.7878" ry="18"/>+<text text-anchor="middle" x="246" y="-436.3" font-family="sans-serif" font-size="8.00">PixelRGB8</text>+</g>+<!-- PixelRGBA8 -->+<g id="node6" class="node"><title>PixelRGBA8</title>+<ellipse fill="none" stroke="black" cx="441" cy="-354" rx="34.8223" ry="18"/>+<text text-anchor="middle" x="441" y="-352.3" font-family="sans-serif" font-size="8.00">PixelRGBA8</text>+</g>+<!-- PixelRGB8&#45;&gt;PixelRGBA8 -->+<g id="edge4" class="edge"><title>PixelRGB8&#45;&gt;PixelRGBA8</title>+<path fill="none" stroke="black" d="M274.25,-429.506C295.421,-422.833 324.934,-412.874 350,-402 351.523,-401.339 381.994,-385.244 407.261,-371.871"/>+<polygon fill="black" stroke="black" points="409.071,-374.874 416.272,-367.102 405.796,-368.687 409.071,-374.874"/>+<text text-anchor="middle" x="393" y="-394.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- PixelRGBF -->+<g id="node8" class="node"><title>PixelRGBF</title>+<ellipse fill="none" stroke="black" cx="33" cy="-102" rx="31.7878" ry="18"/>+<text text-anchor="middle" x="33" y="-100.3" font-family="sans-serif" font-size="8.00">PixelRGBF</text>+</g>+<!-- PixelRGB8&#45;&gt;PixelRGBF -->+<g id="edge6" class="edge"><title>PixelRGB8&#45;&gt;PixelRGBF</title>+<path fill="none" stroke="black" d="M214.594,-434.005C148.276,-424.689 0,-398.847 0,-354 0,-354 0,-354 0,-186 0,-165.437 8.67323,-143.88 17.1268,-127.753"/>+<polygon fill="black" stroke="black" points="20.228,-129.378 22.0115,-118.934 14.1045,-125.986 20.228,-129.378"/>+<text text-anchor="middle" x="23" y="-268.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel8 -->+<g id="node20" class="node"><title>Pixel8</title>+<ellipse fill="none" stroke="black" cx="308" cy="-270" rx="27" ry="18"/>+<text text-anchor="middle" x="308" y="-268.3" font-family="sans-serif" font-size="8.00">Pixel8</text>+</g>+<!-- PixelRGB8&#45;&gt;Pixel8 -->+<g id="edge60" class="edge"><title>PixelRGB8&#45;&gt;Pixel8</title>+<path fill="none" stroke="black" d="M214.516,-433.811C149.341,-423.852 9.37863,-395.049 55,-336 81.2893,-301.973 207.23,-282.163 270.929,-274.163"/>+<polygon fill="black" stroke="black" points="271.46,-277.624 280.961,-272.936 270.61,-270.676 271.46,-277.624"/>+<text text-anchor="middle" x="80" y="-352.3" font-family="sans-serif" font-size="8.00">computeLuma</text>+</g>+<!-- PixelCMYK8 -->+<g id="node28" class="node"><title>PixelCMYK8</title>+<ellipse fill="none" stroke="black" cx="253" cy="-354" rx="34.8223" ry="18"/>+<text text-anchor="middle" x="253" y="-352.3" font-family="sans-serif" font-size="8.00">PixelCMYK8</text>+</g>+<!-- PixelRGB8&#45;&gt;PixelCMYK8 -->+<g id="edge40" class="edge"><title>PixelRGB8&#45;&gt;PixelCMYK8</title>+<path fill="none" stroke="black" d="M224.748,-423.894C213.935,-414.831 204.182,-402.459 210,-390 212.758,-384.095 217.013,-378.817 221.794,-374.241"/>+<polygon fill="black" stroke="black" points="224.225,-376.77 229.601,-367.64 219.705,-371.424 224.225,-376.77"/>+<text text-anchor="middle" x="232" y="-394.3" font-family="sans-serif" font-size="8.00">convertPixel</text>+</g>+<!-- PixelYCbCr8 -->+<g id="node30" class="node"><title>PixelYCbCr8</title>+<ellipse fill="none" stroke="black" cx="150" cy="-354" rx="35.9084" ry="18"/>+<text text-anchor="middle" x="150" y="-352.3" font-family="sans-serif" font-size="8.00">PixelYCbCr8</text>+</g>+<!-- PixelRGB8&#45;&gt;PixelYCbCr8 -->+<g id="edge42" class="edge"><title>PixelRGB8&#45;&gt;PixelYCbCr8</title>+<path fill="none" stroke="black" d="M214.399,-434.415C177.053,-429.451 118.29,-419.109 105,-402 97.106,-391.837 104.874,-381.324 116.049,-372.736"/>+<polygon fill="black" stroke="black" points="118.35,-375.401 124.612,-366.856 114.387,-369.631 118.35,-375.401"/>+<text text-anchor="middle" x="127" y="-394.3" font-family="sans-serif" font-size="8.00">convertPixel</text>+</g>+<!-- PixelRGB16 -->+<g id="node2" class="node"><title>PixelRGB16</title>+<ellipse fill="none" stroke="black" cx="429" cy="-102" rx="34.1425" ry="18"/>+<text text-anchor="middle" x="429" y="-100.3" font-family="sans-serif" font-size="8.00">PixelRGB16</text>+</g>+<!-- PixelRGBA16 -->+<g id="node4" class="node"><title>PixelRGBA16</title>+<ellipse fill="none" stroke="black" cx="343" cy="-18" rx="37.1721" ry="18"/>+<text text-anchor="middle" x="343" y="-16.3" font-family="sans-serif" font-size="8.00">PixelRGBA16</text>+</g>+<!-- PixelRGB16&#45;&gt;PixelRGBA16 -->+<g id="edge2" class="edge"><title>PixelRGB16&#45;&gt;PixelRGBA16</title>+<path fill="none" stroke="black" d="M400.098,-92.2134C386.67,-86.4097 371.409,-77.8363 361,-66 355.909,-60.2106 352.202,-52.8433 349.526,-45.6287"/>+<polygon fill="black" stroke="black" points="352.846,-44.5187 346.482,-36.0479 346.174,-46.6383 352.846,-44.5187"/>+<text text-anchor="middle" x="384" y="-58.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel16 -->+<g id="node16" class="node"><title>Pixel16</title>+<ellipse fill="none" stroke="black" cx="296" cy="-186" rx="27" ry="18"/>+<text text-anchor="middle" x="296" y="-184.3" font-family="sans-serif" font-size="8.00">Pixel16</text>+</g>+<!-- PixelRGB16&#45;&gt;Pixel16 -->+<g id="edge58" class="edge"><title>PixelRGB16&#45;&gt;Pixel16</title>+<path fill="none" stroke="black" d="M424.573,-120.149C421.265,-130.199 415.765,-142.199 407,-150 386.436,-168.304 356.427,-177.32 332.795,-181.75"/>+<polygon fill="black" stroke="black" points="332.056,-178.325 322.773,-183.431 333.214,-185.228 332.056,-178.325"/>+<text text-anchor="middle" x="441" y="-142.3" font-family="sans-serif" font-size="8.00">computeLuma</text>+</g>+<!-- PixelCMYK16 -->+<g id="node26" class="node"><title>PixelCMYK16</title>+<ellipse fill="none" stroke="black" cx="553" cy="-18" rx="37.1721" ry="18"/>+<text text-anchor="middle" x="553" y="-16.3" font-family="sans-serif" font-size="8.00">PixelCMYK16</text>+</g>+<!-- PixelRGB16&#45;&gt;PixelCMYK16 -->+<g id="edge38" class="edge"><title>PixelRGB16&#45;&gt;PixelCMYK16</title>+<path fill="none" stroke="black" d="M451.832,-88.3606C462.615,-81.7841 475.593,-73.6731 487,-66 499.46,-57.6189 512.915,-47.9503 524.406,-39.4968"/>+<polygon fill="black" stroke="black" points="526.807,-42.074 532.761,-33.3106 522.641,-36.4482 526.807,-42.074"/>+<text text-anchor="middle" x="525" y="-58.3" font-family="sans-serif" font-size="8.00">convertPixel</text>+</g>+<!-- PixelRGBA16&#45;&gt;PixelRGB16 -->+<g id="edge44" class="edge"><title>PixelRGBA16&#45;&gt;PixelRGB16</title>+<path fill="none" stroke="black" d="M371.789,-29.3863C383.871,-35.2918 397.329,-43.4955 407,-54 412.378,-59.8422 416.636,-67.2266 419.915,-74.4406"/>+<polygon fill="black" stroke="black" points="416.794,-76.0584 423.794,-84.0115 423.282,-73.4291 416.794,-76.0584"/>+<text text-anchor="middle" x="449" y="-58.3" font-family="sans-serif" font-size="8.00">dropTransparency</text>+</g>+<!-- PixelRGBA8&#45;&gt;PixelRGB8 -->+<g id="edge46" class="edge"><title>PixelRGBA8&#45;&gt;PixelRGB8</title>+<path fill="none" stroke="black" d="M437.029,-372.038C433.845,-382.309 428.318,-394.571 419,-402 399.083,-417.88 332.208,-428.306 287.678,-433.672"/>+<polygon fill="black" stroke="black" points="287.239,-430.2 277.712,-434.834 288.05,-437.153 287.239,-430.2"/>+<text text-anchor="middle" x="463" y="-394.3" font-family="sans-serif" font-size="8.00">dropTransparency</text>+</g>+<!-- PixelRGBA8&#45;&gt;Pixel8 -->+<g id="edge52" class="edge"><title>PixelRGBA8&#45;&gt;Pixel8</title>+<path fill="none" stroke="black" d="M413.829,-342.599C399.476,-336.131 381.815,-327.451 367,-318 354.878,-310.267 342.394,-300.428 332.048,-291.673"/>+<polygon fill="black" stroke="black" points="334.114,-288.833 324.258,-284.946 329.539,-294.131 334.114,-288.833"/>+<text text-anchor="middle" x="392" y="-310.3" font-family="sans-serif" font-size="8.00">computeLuma</text>+</g>+<!-- PixelF -->+<g id="node14" class="node"><title>PixelF</title>+<ellipse fill="none" stroke="black" cx="106" cy="-186" rx="27" ry="18"/>+<text text-anchor="middle" x="106" y="-184.3" font-family="sans-serif" font-size="8.00">PixelF</text>+</g>+<!-- PixelRGBF&#45;&gt;PixelF -->+<g id="edge56" class="edge"><title>PixelRGBF&#45;&gt;PixelF</title>+<path fill="none" stroke="black" d="M27.0137,-120.147C24.9943,-129.716 24.4635,-141.251 30,-150 38.9021,-164.067 54.9514,-172.745 70.015,-178.044"/>+<polygon fill="black" stroke="black" points="69.0981,-181.424 79.6867,-181.06 71.1825,-174.741 69.0981,-181.424"/>+<text text-anchor="middle" x="55" y="-142.3" font-family="sans-serif" font-size="8.00">computeLuma</text>+</g>+<!-- PixelYA16 -->+<g id="node9" class="node"><title>PixelYA16</title>+<ellipse fill="none" stroke="black" cx="259" cy="-102" rx="31.7878" ry="18"/>+<text text-anchor="middle" x="259" y="-100.3" font-family="sans-serif" font-size="8.00">PixelYA16</text>+</g>+<!-- PixelYA16&#45;&gt;PixelRGBA16 -->+<g id="edge8" class="edge"><title>PixelYA16&#45;&gt;PixelRGBA16</title>+<path fill="none" stroke="black" d="M258.152,-83.5434C258.613,-73.883 260.687,-62.36 267,-54 275.273,-43.0444 287.722,-35.28 300.163,-29.8405"/>+<polygon fill="black" stroke="black" points="301.586,-33.0421 309.607,-26.1198 299.02,-26.5294 301.586,-33.0421"/>+<text text-anchor="middle" x="290" y="-58.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- PixelYA16&#45;&gt;Pixel16 -->+<g id="edge48" class="edge"><title>PixelYA16&#45;&gt;Pixel16</title>+<path fill="none" stroke="black" d="M229.98,-109.854C206.015,-117.845 177.897,-131.656 191,-150 199.293,-161.611 233.125,-171.835 260.175,-178.391"/>+<polygon fill="black" stroke="black" points="259.609,-181.853 270.143,-180.72 261.201,-175.037 259.609,-181.853"/>+<text text-anchor="middle" x="225" y="-142.3" font-family="sans-serif" font-size="8.00">dropTransparency</text>+</g>+<!-- PixelYA8 -->+<g id="node11" class="node"><title>PixelYA8</title>+<ellipse fill="none" stroke="black" cx="439" cy="-186" rx="28.8445" ry="18"/>+<text text-anchor="middle" x="439" y="-184.3" font-family="sans-serif" font-size="8.00">PixelYA8</text>+</g>+<!-- PixelYA8&#45;&gt;PixelRGB8 -->+<g id="edge12" class="edge"><title>PixelYA8&#45;&gt;PixelRGB8</title>+<path fill="none" stroke="black" d="M464.762,-194.278C489.627,-203.81 525.815,-222.121 542,-252 574.918,-312.769 554.613,-358.387 501,-402 484.752,-415.217 355.671,-428.373 287.639,-434.476"/>+<polygon fill="black" stroke="black" points="287.321,-430.99 277.669,-435.359 287.938,-437.963 287.321,-430.99"/>+<text text-anchor="middle" x="580" y="-310.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- PixelYA8&#45;&gt;PixelRGBA8 -->+<g id="edge10" class="edge"><title>PixelYA8&#45;&gt;PixelRGBA8</title>+<path fill="none" stroke="black" d="M460.035,-198.84C467.824,-204.817 475.781,-212.68 480,-222 497.598,-260.871 496.093,-278.904 479,-318 476.725,-323.204 473.295,-328.013 469.411,-332.309"/>+<polygon fill="black" stroke="black" points="466.792,-329.975 462.079,-339.464 471.681,-334.985 466.792,-329.975"/>+<text text-anchor="middle" x="515" y="-268.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- PixelYA8&#45;&gt;Pixel8 -->+<g id="edge50" class="edge"><title>PixelYA8&#45;&gt;Pixel8</title>+<path fill="none" stroke="black" d="M412.102,-193.018C393.267,-198.789 368.145,-208.269 349,-222 340.05,-228.419 331.808,-237.196 325.098,-245.49"/>+<polygon fill="black" stroke="black" points="322.247,-243.456 318.929,-253.518 327.797,-247.721 322.247,-243.456"/>+<text text-anchor="middle" x="383" y="-226.3" font-family="sans-serif" font-size="8.00">dropTransparency</text>+</g>+<!-- PixelYA8&#45;&gt;Pixel8 -->+<g id="edge62" class="edge"><title>PixelYA8&#45;&gt;Pixel8</title>+<path fill="none" stroke="black" d="M434.558,-204.131C431.244,-214.175 425.744,-226.175 417,-234 397.039,-251.864 367.99,-260.893 344.899,-265.441"/>+<polygon fill="black" stroke="black" points="344.002,-262.045 334.763,-267.23 345.219,-268.938 344.002,-262.045"/>+<text text-anchor="middle" x="451" y="-226.3" font-family="sans-serif" font-size="8.00">computeLuma</text>+</g>+<!-- PixelF&#45;&gt;PixelRGBF -->+<g id="edge14" class="edge"><title>PixelF&#45;&gt;PixelRGBF</title>+<path fill="none" stroke="black" d="M100.45,-168.312C96.8232,-158.653 91.3784,-146.886 84,-138 78.5966,-131.492 71.7476,-125.606 64.8367,-120.568"/>+<polygon fill="black" stroke="black" points="66.3898,-117.392 56.1539,-114.657 62.4504,-123.178 66.3898,-117.392"/>+<text text-anchor="middle" x="115" y="-142.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel16&#45;&gt;PixelRGB16 -->+<g id="edge18" class="edge"><title>Pixel16&#45;&gt;PixelRGB16</title>+<path fill="none" stroke="black" d="M313.43,-172.019C326.239,-162.023 344.257,-148.532 361,-138 371.689,-131.277 383.756,-124.61 394.725,-118.875"/>+<polygon fill="black" stroke="black" points="396.336,-121.982 403.628,-114.295 393.134,-115.757 396.336,-121.982"/>+<text text-anchor="middle" x="384" y="-142.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel16&#45;&gt;PixelRGBA16 -->+<g id="edge16" class="edge"><title>Pixel16&#45;&gt;PixelRGBA16</title>+<path fill="none" stroke="black" d="M305.084,-168.741C307.903,-162.903 310.835,-156.272 313,-150 325.048,-115.091 333.609,-73.3023 338.424,-46.2333"/>+<polygon fill="black" stroke="black" points="341.89,-46.732 340.143,-36.2822 334.992,-45.5405 341.89,-46.732"/>+<text text-anchor="middle" x="353" y="-100.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel16&#45;&gt;PixelYA16 -->+<g id="edge20" class="edge"><title>Pixel16&#45;&gt;PixelYA16</title>+<path fill="none" stroke="black" d="M280.78,-170.7C275.635,-164.701 270.35,-157.469 267,-150 264.277,-143.93 262.473,-137.052 261.279,-130.438"/>+<polygon fill="black" stroke="black" points="264.727,-129.827 259.862,-120.415 257.796,-130.807 264.727,-129.827"/>+<text text-anchor="middle" x="290" y="-142.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel8&#45;&gt;PixelRGB8 -->+<g id="edge24" class="edge"><title>Pixel8&#45;&gt;PixelRGB8</title>+<path fill="none" stroke="black" d="M312.919,-288.095C319.136,-314.953 326.712,-366.057 306,-402 300.622,-411.332 291.773,-418.518 282.501,-423.926"/>+<polygon fill="black" stroke="black" points="280.83,-420.85 273.581,-428.577 284.067,-427.057 280.83,-420.85"/>+<text text-anchor="middle" x="342" y="-352.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel8&#45;&gt;PixelRGBA8 -->+<g id="edge22" class="edge"><title>Pixel8&#45;&gt;PixelRGBA8</title>+<path fill="none" stroke="black" d="M334.939,-272.185C360.073,-275.561 397.177,-284.15 421,-306 426.855,-311.37 431.048,-318.72 434.03,-326.054"/>+<polygon fill="black" stroke="black" points="430.836,-327.531 437.387,-335.858 437.458,-325.263 430.836,-327.531"/>+<text text-anchor="middle" x="452" y="-310.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel8&#45;&gt;PixelYA8 -->+<g id="edge26" class="edge"><title>Pixel8&#45;&gt;PixelYA8</title>+<path fill="none" stroke="black" d="M290.77,-255.865C280.734,-245.919 271.57,-232.553 280,-222 287.559,-212.538 356.092,-199.635 400.682,-192.122"/>+<polygon fill="black" stroke="black" points="401.335,-195.562 410.626,-190.469 400.187,-188.657 401.335,-195.562"/>+<text text-anchor="middle" x="303" y="-226.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel8&#45;&gt;PixelF -->+<g id="edge28" class="edge"><title>Pixel8&#45;&gt;PixelF</title>+<path fill="none" stroke="black" d="M281.145,-267.812C249.741,-264.331 196.969,-255.554 157,-234 145.577,-227.84 134.814,-218.473 126.146,-209.643"/>+<polygon fill="black" stroke="black" points="128.492,-207.028 119.104,-202.117 123.381,-211.81 128.492,-207.028"/>+<text text-anchor="middle" x="180" y="-226.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- Pixel8&#45;&gt;Pixel16 -->+<g id="edge30" class="edge"><title>Pixel8&#45;&gt;Pixel16</title>+<path fill="none" stroke="black" d="M282.206,-263.957C256.398,-257.327 219.359,-246.018 211,-234 196.025,-212.469 230.216,-199.429 259.604,-192.482"/>+<polygon fill="black" stroke="black" points="260.687,-195.828 269.71,-190.275 259.193,-188.989 260.687,-195.828"/>+<text text-anchor="middle" x="234" y="-226.3" font-family="sans-serif" font-size="8.00">promotePixel</text>+</g>+<!-- PixelCMYK16&#45;&gt;PixelRGB16 -->+<g id="edge32" class="edge"><title>PixelCMYK16&#45;&gt;PixelRGB16</title>+<path fill="none" stroke="black" d="M564.03,-35.4654C568.707,-45.2873 571.707,-57.2873 565,-66 553.678,-80.7091 507.858,-90.6423 472.496,-96.2857"/>+<polygon fill="black" stroke="black" points="471.73,-92.862 462.373,-97.8319 472.787,-99.7817 471.73,-92.862"/>+<text text-anchor="middle" x="590" y="-58.3" font-family="sans-serif" font-size="8.00">convertPixel</text>+</g>+<!-- PixelCMYK8&#45;&gt;PixelRGB8 -->+<g id="edge34" class="edge"><title>PixelCMYK8&#45;&gt;PixelRGB8</title>+<path fill="none" stroke="black" d="M257.173,-372.299C258.645,-381.28 259.584,-392.241 258,-402 257.535,-404.865 256.875,-407.811 256.1,-410.724"/>+<polygon fill="black" stroke="black" points="252.737,-409.753 253.143,-420.34 259.427,-411.811 252.737,-409.753"/>+<text text-anchor="middle" x="280" y="-394.3" font-family="sans-serif" font-size="8.00">convertPixel</text>+</g>+<!-- PixelYCbCr8&#45;&gt;PixelRGB8 -->+<g id="edge36" class="edge"><title>PixelYCbCr8&#45;&gt;PixelRGB8</title>+<path fill="none" stroke="black" d="M149.836,-372.162C150.645,-381.97 153.174,-393.727 160,-402 171.431,-415.854 189.112,-424.462 205.475,-429.768"/>+<polygon fill="black" stroke="black" points="204.847,-433.23 215.426,-432.663 206.802,-426.509 204.847,-433.23"/>+<text text-anchor="middle" x="182" y="-394.3" font-family="sans-serif" font-size="8.00">convertPixel</text>+</g>+<!-- PixelYCbCr8&#45;&gt;Pixel8 -->+<g id="edge54" class="edge"><title>PixelYCbCr8&#45;&gt;Pixel8</title>+<path fill="none" stroke="black" d="M174.688,-340.875C202.411,-326.136 247.432,-302.201 277.376,-286.281"/>+<polygon fill="black" stroke="black" points="279.34,-289.201 286.527,-281.416 276.054,-283.02 279.34,-289.201"/>+<text text-anchor="middle" x="263" y="-310.3" font-family="sans-serif" font-size="8.00">computeLuma</text>+</g>+</g>+</svg>
src/Codec/Picture/Bitmap.hs view
@@ -517,7 +517,11 @@ 
 -- | Helper method to cast a 'B.ByteString' to a 'VS.Vector' of some type.
 castByteString :: VS.Storable a => B.ByteString -> VS.Vector a
+#if MIN_VERSION_bytestring(0,11,0)
+castByteString (BI.BS fp len) = VS.unsafeCast $ VS.unsafeFromForeignPtr fp 0 len
+#else
 castByteString (BI.PS fp offset len) = VS.unsafeCast $ VS.unsafeFromForeignPtr fp offset len
+#endif
 
 decodeImageRGBA8 :: RGBABmpFormat -> BmpV5Header -> B.ByteString -> Image PixelRGBA8
 decodeImageRGBA8 pixelFormat (BmpV5Header { width = w, height = h, bitPerPixel = bpp }) str = Image wi hi stArray where
src/Codec/Picture/ColorQuant.hs view
@@ -278,7 +278,7 @@ 
 -- Based on the OCaml implementation:
 -- http://rosettacode.org/wiki/Color_quantization
--- which is in turn based on: www.leptonica.com/papers/mediancut.pdf.
+-- which is in turn based on: www.leptonica.org/papers/mediancut.pdf.
 -- We use the product of volume and population to determine the next cluster
 -- to split and determine the placement of each color by compating it to the
 -- mean of the parent cluster. So median cut is a bit of a misnomer, since one
src/Codec/Picture/ConvGraph.hs view
@@ -1,10 +1,10 @@--- $graph
---
--- The following graph describe the differents way to convert between pixel types,
---
--- * Nodes describe pixel type
---
--- * Arrows describe functions
---
--- <<docimages/pixelgraph.svg>>
---
+-- $graph+--+-- The following graph describe the differents way to convert between pixel types,+--+-- * Nodes describe pixel type+--+-- * Arrows describe functions+--+-- <<docimages/pixelgraph.svg>>+--
src/Codec/Picture/InternalHelper.hs view
@@ -13,39 +13,20 @@                       )
 import qualified Data.Binary.Get as G
 
-#if MIN_VERSION_binary(0,6,4)
-#else
-import Control.Applicative( (<$>) )
-import qualified Control.Exception as E
--- I feel so dirty. :(
-import System.IO.Unsafe( unsafePerformIO )
-#endif
-
 decode :: (Binary a) => B.ByteString -> Either String a
 decode = runGetStrict get
 
 runGet :: Get a -> L.ByteString -> Either String a
-#if MIN_VERSION_binary(0,6,4)
 runGet act = unpack . G.runGetOrFail act
     where unpack (Left (_, _, str)) = Left str
           unpack (Right (_, _, element)) = Right element
-#else
-runGet act str = unsafePerformIO $ E.catch
-    (Right <$> E.evaluate (G.runGet act str))
-    (\msg -> return . Left $ show (msg :: E.SomeException))
-#endif
 
 runGetStrict :: Get a -> B.ByteString -> Either String a
 runGetStrict act buffer = runGet act $ L.fromChunks [buffer]
 
 getRemainingBytes :: Get B.ByteString
-getRemainingBytes = do
-    rest <- getRemainingLazyByteString 
-    return $ case L.toChunks rest of
-        [] -> B.empty
-        [a] -> a
-        lst -> B.concat lst
+getRemainingBytes = L.toStrict <$> getRemainingLazyByteString
 
 getRemainingLazyBytes :: Get L.ByteString
-getRemainingLazyBytes = getRemainingLazyByteString 
+getRemainingLazyBytes = getRemainingLazyByteString
 
src/Codec/Picture/Jpg.hs view
@@ -422,9 +422,16 @@   forM_ lst $ \(params, str) -> do
     let componentsInfo = V.fromList params
         compReader = initBoolStateJpg . B.concat $ L.toChunks str
-        maxiW = maximum [fst $ subSampling c | (c,_) <- params]
-        maxiH = maximum [snd $ subSampling c | (c,_) <- params]
+        maxiSubSampW = maximum [fst $ subSampling c | (c,_) <- params]
+        maxiSubSampH = maximum [snd $ subSampling c | (c,_) <- params]
 
+        (maxiW, maxiH) = 
+            if length params > 1 then
+                (maximum [componentWidth c | (c,_) <- params], 
+                    maximum [componentHeight c | (c,_) <- params])
+            else
+                (maxiSubSampW, maxiSubSampH)
+
         imageBlockWidth = toBlockSize imgWidth
         imageBlockHeight = toBlockSize imgHeight
 
@@ -574,7 +581,7 @@        let (st, arr) = decodeBaseline
            jfifMeta = foldMap extractMetadatas $ app0JFifMarker st
            exifMeta = foldMap extractTiffMetadata $ app1ExifMarker st
-           meta = sizeMeta <> jfifMeta <> exifMeta
+           meta = jfifMeta <> exifMeta <> sizeMeta
        in
        (, meta) <$>
            dynamicOfColorSpace (colorSpaceOfState st) imgWidth imgHeight arr
@@ -582,7 +589,7 @@        let (st, arr) = decodeProgressive
            jfifMeta = foldMap extractMetadatas $ app0JFifMarker st
            exifMeta = foldMap extractTiffMetadata $ app1ExifMarker st
-           meta = sizeMeta <> jfifMeta <> exifMeta
+           meta = jfifMeta <> exifMeta <> sizeMeta
        in
        (, meta) <$>
            dynamicOfColorSpace (colorSpaceOfState st) imgWidth imgHeight arr
src/Codec/Picture/Jpg/Internal/DefaultTable.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE FlexibleContexts #-}
 -- | Module used by the jpeg decoder internally, shouldn't be used
@@ -33,6 +34,7 @@                                      , defaultDcLumaHuffmanTable
                                      ) where
 
+import Control.DeepSeq( NFData(..) )
 import Data.Int( Int16 )
 import Foreign.Storable ( Storable )
 import Control.Monad.ST( runST )
@@ -42,6 +44,7 @@ import Data.Word( Word8, Word16 )
 import Data.List( foldl' )
 import qualified Data.Vector.Storable.Mutable as M
+import GHC.Generics( Generic )
 
 import Codec.Picture.BitWriter
 
@@ -108,7 +111,8 @@ 
 -- | Enumeration used to search in the tables for different components.
 data DctComponent = DcComponent | AcComponent
-    deriving (Eq, Show)
+    deriving (Eq, Show, Generic)
+instance NFData DctComponent
 
 -- | Transform parsed coefficients from the jpeg header to a
 -- tree which can be used to decode data.
src/Codec/Picture/Jpg/Internal/Types.hs view
@@ -1,6 +1,13 @@+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE CPP #-}
+
+-- | A good explanation of the JPEG format, including diagrams, is given at:
+-- <https://github.com/corkami/formats/blob/master/image/jpeg.md>
+--
+-- The full spec (excluding EXIF): https://www.w3.org/Graphics/JPEG/itu-t81.pdf
 module Codec.Picture.Jpg.Internal.Types( MutableMacroBlock
                               , createEmptyMutableMacroBlock
                               , printMacroBlock
@@ -21,8 +28,19 @@                               , JpgAdobeApp14( .. )
                               , JpgJFIFApp0( .. )
                               , JFifUnit( .. )
+                              , TableList( .. )
+                              , RestartInterval( .. )
+                              , getJpgImage
                               , calculateSize
                               , dctBlockSize
+                              , parseECS
+                              , parseECS_simple
+                              , skipUntilFrames
+                              , skipFrameMarker
+                              , parseFrameOfKind
+                              , parseFrames
+                              , parseFrameKinds
+                              , parseToFirstFrameHeader
                               ) where
 
 
@@ -30,10 +48,13 @@ import Control.Applicative( pure, (<*>), (<$>) )
 #endif
 
+import Control.DeepSeq( NFData(..) )
 import Control.Monad( when, replicateM, forM, forM_, unless )
 import Control.Monad.ST( ST )
 import Data.Bits( (.|.), (.&.), unsafeShiftL, unsafeShiftR )
 import Data.List( partition )
+import Data.Maybe( maybeToList )
+import GHC.Generics( Generic )
 
 #if !MIN_VERSION_base(4,11,0)
 import Data.Monoid( (<>) )
@@ -48,8 +69,9 @@ import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as BC
 import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString.Unsafe as BU
 
-import Data.Int( Int16 )
+import Data.Int( Int16, Int64 )
 import Data.Word(Word8, Word16 )
 import Data.Binary( Binary(..) )
 
@@ -59,7 +81,11 @@                       , getByteString
                       , skip
                       , bytesRead
+                      , lookAhead
+                      , ByteOffset
+                      , getLazyByteString
                       )
+import qualified Data.Binary.Get.Internal as GetInternal
 
 import Data.Binary.Put( Put
                       , putWord8
@@ -75,7 +101,6 @@ import Codec.Picture.Tiff.Internal.Metadata( exifOffsetIfd )
 import Codec.Picture.Metadata.Exif
 
-{-import Debug.Trace-}
 import Text.Printf
 
 -- | Type only used to make clear what kind of integer we are carrying
@@ -108,7 +133,8 @@ 
     | JpgRestartInterval
     | JpgRestartIntervalEnd Word8
-    deriving (Eq, Show)
+    deriving (Eq, Show, Generic)
+instance NFData JpgFrameKind
 
 data JpgFrame =
       JpgAppFrame        !Word8 B.ByteString
@@ -118,10 +144,11 @@     | JpgExtension       !Word8 B.ByteString
     | JpgQuantTable      ![JpgQuantTableSpec]
     | JpgHuffmanTable    ![(JpgHuffmanTableSpec, HuffmanPackedTree)]
-    | JpgScanBlob        !JpgScanHeader !L.ByteString
+    | JpgScanBlob        !JpgScanHeader !L.ByteString -- ^ The @ByteString@ is the ECS (Entropy-Coded Segment), typically the largest part of compressed image data.
     | JpgScans           !JpgFrameKind !JpgFrameHeader
     | JpgIntervalRestart !Word16
-    deriving Show
+    deriving (Eq, Show, Generic)
+instance NFData JpgFrame
 
 data JpgColorSpace
   = JpgColorSpaceYCbCr
@@ -133,13 +160,15 @@   | JpgColorSpaceCMYK
   | JpgColorSpaceRGB
   | JpgColorSpaceRGBA
-  deriving Show
+  deriving (Eq, Show, Generic)
+instance NFData JpgColorSpace
 
 data AdobeTransform
   = AdobeUnknown    -- ^ Value 0
   | AdobeYCbCr      -- ^ value 1
   | AdobeYCck       -- ^ value 2
-  deriving Show
+  deriving (Eq, Show, Generic)
+instance NFData AdobeTransform
 
 data JpgAdobeApp14 = JpgAdobeApp14
   { _adobeDctVersion :: !Word16
@@ -147,14 +176,16 @@   , _adobeFlag1      :: !Word16
   , _adobeTransform  :: !AdobeTransform
   }
-  deriving Show
+  deriving (Eq, Show, Generic)
+instance NFData JpgAdobeApp14
 
 -- | Size: 1
 data JFifUnit
   = JFifUnitUnknown   -- ^ 0
   | JFifPixelsPerInch -- ^ 1
   | JFifPixelsPerCentimeter -- ^ 2
-  deriving Show
+  deriving (Eq, Show, Generic)
+instance NFData JFifUnit
 
 instance Binary JFifUnit where
   put v = putWord8 $ case v of
@@ -175,7 +206,8 @@   , _jfifDpiY      :: !Word16
   , _jfifThumbnail :: !(Maybe {- (Image PixelRGB8) -} Int)
   }
-  deriving Show
+  deriving (Eq, Show, Generic)
+instance NFData JpgJFIFApp0
 
 instance Binary JpgJFIFApp0 where
   get = do
@@ -260,7 +292,8 @@     , jpgImageComponentCount :: !Word8
     , jpgComponents          :: ![JpgComponent]
     }
-    deriving Show
+    deriving (Eq, Show, Generic)
+instance NFData JpgFrameHeader
 
 
 instance SizeCalculable JpgFrameHeader where
@@ -275,13 +308,15 @@     , verticalSamplingFactor    :: !Word8
     , quantizationTableDest     :: !Word8
     }
-    deriving Show
+    deriving (Eq, Show, Generic)
+instance NFData JpgComponent
 
 instance SizeCalculable JpgComponent where
     calculateSize _ = 3
 
 data JpgImage = JpgImage { jpgFrame :: [JpgFrame] }
-    deriving Show
+    deriving (Eq, Show, Generic)
+instance NFData JpgImage
 
 data JpgScanSpecification = JpgScanSpecification
     { componentSelector :: !Word8
@@ -291,7 +326,8 @@     , acEntropyCodingTable :: !Word8
 
     }
-    deriving Show
+    deriving (Eq, Show, Generic)
+instance NFData JpgScanSpecification
 
 instance SizeCalculable JpgScanSpecification where
     calculateSize _ = 2
@@ -310,7 +346,8 @@       -- | Encoded as 4 bits
     , successiveApproxLow :: !Word8
     }
-    deriving Show
+    deriving (Eq, Show, Generic)
+instance NFData JpgScanHeader
 
 instance SizeCalculable JpgScanHeader where
     calculateSize hdr = 2 + 1
@@ -327,7 +364,8 @@ 
     , quantTable         :: MacroBlock Int16
     }
-    deriving Show
+    deriving (Eq, Show, Generic)
+instance NFData JpgQuantTableSpec
 
 class SizeCalculable a where
     calculateSize :: a -> Int
@@ -382,7 +420,8 @@     , huffSizes :: !(VU.Vector Word8)
     , huffCodes :: !(V.Vector (VU.Vector Word8))
     }
-    deriving Show
+    deriving (Eq, Show, Generic)
+instance NFData JpgHuffmanTableSpec
 
 instance SizeCalculable JpgHuffmanTableSpec where
     calculateSize table = 1 + 16 + sum [fromIntegral e | e <- VU.toList $ huffSizes table]
@@ -416,15 +455,28 @@         putWord8 0xFF >> putWord8 0xD8 >> mapM_ putFrame frames
             >> putWord8 0xFF >> putWord8 0xD9
 
+    -- | Consider using `getJpgImage` instead for a non-semi-lazy implementation.
     get = do
-        let startOfImageMarker = 0xD8
-            -- endOfImageMarker = 0xD9
-        checkMarker commonMarkerFirstByte startOfImageMarker
-        eatUntilCode
-        frames <- parseFrames
+        skipUntilFrames
+        frames <- parseFramesSemiLazy
+        -- let endOfImageMarker = 0xD9
         {-checkMarker commonMarkerFirstByte endOfImageMarker-}
         return JpgImage { jpgFrame = frames }
 
+-- | Like `get` from `instance Binary JpgImage`, but without the legacy
+-- semi-lazy implementation.
+getJpgImage :: Get JpgImage
+getJpgImage = do
+    skipUntilFrames
+    frames <- parseFrames
+    return JpgImage { jpgFrame = frames }
+
+skipUntilFrames :: Get ()
+skipUntilFrames = do
+    let startOfImageMarker = 0xD8
+    checkMarker commonMarkerFirstByte startOfImageMarker
+    eatUntilCode
+
 eatUntilCode :: Get ()
 eatUntilCode = do
     code <- getWord8
@@ -436,7 +488,7 @@     getByteString (fromIntegral size - 2)
 
 putFrame :: JpgFrame -> Put
-putFrame (JpgAdobeAPP14 adobe) = 
+putFrame (JpgAdobeAPP14 adobe) =
     put (JpgAppSegment 14) >> putWord16be 14 >> put adobe
 putFrame (JpgJFIF jfif) =
     put (JpgAppSegment 0) >> putWord16be (14+2) >> put jfif
@@ -469,41 +521,162 @@     when (rb1 /= b1 || rb2 /= b2)
          (fail "Invalid marker used")
 
-extractScanContent :: L.ByteString -> (L.ByteString, L.ByteString)
-extractScanContent str = aux 0
-  where maxi = fromIntegral $ L.length str - 1
+-- | Simpler implementation of `parseECS` to allow an easier understanding
+-- of the logic, and to provide a comparison for correctness.
+parseECS_simple :: Get L.ByteString
+parseECS_simple = do
+    -- There's no efficient way in `binary` to parse byte-by-byte while assembling a
+    -- resulting ByteString (without using `.Internal` modules, which is what
+    --  `parseECS` does), so instead first compute the length of the content
+    -- byte-by-byte inside a `lookAhead` (not advancing the parser offset), and
+    -- then efficiently take that long a ByteString (advancing the parser offset).
+    --
+    -- This is still slow compared to `parseECS` because parser functions
+    -- (`getWord8`) are used repeatedly, instead of plain loops over ByteString contents.
+    -- The slowdown is ~2x on GHC 8.10.7 on an Intel Core i7-7500U.
+    n <- lookAhead getContentLength
+    getLazyByteString n
+  where
+    getContentLength :: Get ByteOffset
+    getContentLength = do
+        bytesReadBeforeContent <- bytesRead
+        let loop :: Word8 -> Get ByteOffset
+            loop !v = do
+                vNext <- getWord8
+                let isReset = 0xD0 <= vNext && vNext <= 0xD7
+                let vIsSegmentMarker = v == 0xFF && vNext /= 0 && not isReset
+                if not vIsSegmentMarker
+                    then loop vNext
+                    else do
+                        bytesReadAfterContentPlus2 <- bytesRead -- "plus 2" because we've also read the segment marker (0xFF and `vNext`)
+                        let !contentLength = (bytesReadAfterContentPlus2 - 2) - bytesReadBeforeContent
+                        return contentLength
 
-        aux n | n >= maxi = (str, L.empty)
-              | v == 0xFF && vNext /= 0 && not isReset = L.splitAt n str
-              | otherwise = aux (n + 1)
-            where v = str `L.index` n
-                  vNext = str `L.index` (n + 1)
-                  isReset = 0xD0 <= vNext && vNext <= 0xD7
+        v_first <- getWord8
+        loop v_first
 
-parseAdobe14 :: B.ByteString -> [JpgFrame] -> [JpgFrame]
-parseAdobe14 str lst = go where
-  go = case runGetStrict get str of
-    Left _err -> lst
-    Right app14 -> JpgAdobeAPP14 app14 : lst
+-- Replace by `Data.ByteString.dropEnd` once we require `bytestring >= 0.11.1.0`.
+bsDropEnd :: Int -> B.ByteString -> B.ByteString
+bsDropEnd n bs
+    | n <= 0    = bs
+    | n >= len  = B.empty
+    | otherwise = B.take (len - 1) bs
+  where
+    len = B.length bs
+{-# INLINE bsDropEnd #-}
 
+-- | Parses a Scan's ECS (Entropy-Coded Segment, the largest part of compressed image data)
+-- from the `Get` stream.
+--
+-- When this function is called, the parser's offset should be
+-- immediately behind the SOS tag.
+--
+-- As described on e.g. https://www.ccoderun.ca/programming/2017-01-31_jpeg/,
+--
+-- > To find the next segment after the SOS, you must keep reading until you
+-- > find a 0xFF bytes which is not immediately followed by 0x00 (see "byte stuffing")
+-- > [or a reset marker's byte: 0xD0 through 0xD7].
+-- > Normally, this will be the EOI segment that comes at the end of the file.
+--
+-- where the 0xFF is the next segment's marker.
+-- See https://github.com/corkami/formats/blob/master/image/jpeg.md#entropy-coded-segment
+-- for more details.
+--
+-- This function returns the ECS, not including the next segment's
+-- marker on its trailing end.
+parseECS :: Get L.ByteString
+parseECS = do
+    -- For a simpler but slower implementation of this function, see
+    -- `parseECS_simple`.
+
+    v_first <- getWord8
+    -- TODO: Compare with what `scan` from `binary-parsers` does.
+    --       Probably we cannot use it because it does not allow us to set the parser state
+    --       to be _before_ the segment marker which would be convenient to not have to
+    --       make a special case the function that calls this function.
+    --       But `scan` works on pointers into the bytestring chunks. Why, for performance?
+    --       I've asked on https://github.com/winterland1989/binary-parsers/issues/7
+    --       If that is for performance, we may want to replicate the same thing here.
+    --
+    --       An orthogonal idea is to use `Data.ByteString.elemIndex` to fast-forward
+    --       to the next 0xFF using `memchr`, but the `unsafe` call to `memchr` might
+    --       have too much overhead, since 0xFF bytes appear statistically every 256 bytes.
+    --       See https://stackoverflow.com/questions/14519905/how-much-does-it-cost-for-haskell-ffi-to-go-into-c-and-back
+
+    -- `withInputChunks` allows us to work on chunks of ByteStrings,
+    -- reducing the number of higher-overhead `Get` functions called.
+    -- It also allows to easily assemble the ByteString to return,
+    -- which may be cross-chunk.
+    -- `withInputChunks` terminates when we return a
+    --     Right (consumed :: ByteString, unconsumed :: ByteString)
+    -- from `consumeChunk`, setting the `Get` parser's offset to just before `unconsumed`.
+    -- Because the segment marker we seek may be the 2 bytes across chunk boundaries,
+    -- we need to keep a reference to the previous chunk (initialised as `B.empty`),
+    -- so that we can set `consumed` properly, because this function is supposed
+    -- to not consume the start of the segment marker (see code dropping the last
+    -- byte of the previous chunk below).
+    GetInternal.withInputChunks
+        (v_first, B.empty)
+        consumeChunk
+        (         L.fromChunks . (B.singleton v_first :)) -- `v_first` also belongs to the returned BS
+        (return . L.fromChunks . (B.singleton v_first :)) -- `v_first` also belongs to the returned BS
+  where
+    consumeChunk :: GetInternal.Consume (Word8, B.ByteString) -- which is: (Word8, B.ByteString) -> B.ByteString -> Either (Word8, B.ByteString) (B.ByteString, B.ByteString)
+    consumeChunk (!v_chunk_start, !prev_chunk) !chunk
+        -- If `withInputChunks` hands us an empty chunk (which `binary` probably
+        -- won't do, but since that's not documented, handle it anyway) then skip over it,
+        -- so that we always remember the last `prev_chunk` that actually has data in it,
+        -- since we `bsDropEnd 1 prev_chunk` in the `case` below.
+        | B.null chunk = Left (v_chunk_start, prev_chunk)
+        | otherwise = loop v_chunk_start 0
+          where
+            loop :: Word8 -> Int -> Either (Word8, B.ByteString) (B.ByteString, B.ByteString)
+            loop !v !offset_in_chunk
+                | offset_in_chunk >= B.length chunk = Left (v, chunk)
+                | otherwise =
+                    let !vNext = BU.unsafeIndex chunk offset_in_chunk -- bounds check is done above
+                        !isReset = 0xD0 <= vNext && vNext <= 0xD7
+                        !vIsSegmentMarker = v == 0xFF && vNext /= 0 && not isReset
+                    in
+                        if not vIsSegmentMarker
+                            then loop vNext (offset_in_chunk+1)
+                            else
+                                -- Set the parser state to _before_ the segment marker.
+                                -- The first case, where the segment marker's 2 bytes are exactly
+                                -- at the chunk boundary, requires us to allocate a new BS with
+                                -- `B.cons`; luckily this case should be rare.
+                                let (!consumed, !unconsumed) = case () of
+                                     () | offset_in_chunk == 0 -> (bsDropEnd 1 prev_chunk, v `B.cons` chunk) -- segment marker starts at `v`, which is the last byte of the previous chunk
+                                        | offset_in_chunk == 1 -> (B.empty, chunk) -- segment marker starts exactly at `chunk`
+                                        | otherwise            -> B.splitAt (offset_in_chunk - 1) chunk -- segment marker starts at `v`, which is 1 before `vNext` (which is at `offset_in_chunk`)
+                                in Right $! (consumed, unconsumed)
+
+
+
+parseAdobe14 :: B.ByteString -> Maybe JpgFrame
+parseAdobe14 str = case runGetStrict get str of
+    Left _err -> Nothing
+    Right app14 -> Just $! JpgAdobeAPP14 app14
+
 -- | Parse JFIF or JFXX information. Right now only JFIF.
-parseJF__  :: B.ByteString -> [JpgFrame] -> [JpgFrame]
-parseJF__  str lst = go where
-  go = case runGetStrict get str of
-    Left _err -> lst
-    Right jfif -> JpgJFIF jfif : lst
+parseJF__ :: B.ByteString -> Maybe JpgFrame
+parseJF__ str = case runGetStrict get str of
+    Left _err -> Nothing
+    Right jfif -> Just $! JpgJFIF jfif
 
-parseExif :: B.ByteString -> [JpgFrame] -> [JpgFrame]
-parseExif str lst 
-  | exifHeader `B.isPrefixOf` str = go
-  | otherwise = lst
+parseExif :: B.ByteString -> Maybe JpgFrame
+parseExif str
+  | exifHeader `B.isPrefixOf` str =
+      let
+        tiff = B.drop (B.length exifHeader) str
+      in
+        case runGetStrict (getP tiff) tiff of
+            Left _err -> Nothing
+            Right (_hdr :: TiffHeader, []) -> Nothing
+            Right (_hdr :: TiffHeader, ifds : _) -> Just $! JpgExif ifds
+  | otherwise = Nothing
   where
     exifHeader = BC.pack "Exif\0\0"
-    tiff = B.drop (B.length exifHeader) str
-    go = case runGetStrict (getP tiff) tiff of
-      Left _err -> lst
-      Right (_hdr :: TiffHeader, []) -> lst
-      Right (_hdr :: TiffHeader, ifds : _) -> JpgExif ifds : lst
 
 putExif :: [ImageFileDirectory] -> Put
 putExif ifds = putAll where
@@ -515,7 +688,7 @@   ifdList = case partition (isInIFD0 . ifdIdentifier) ifds of
     (ifd0, []) -> [ifd0]
     (ifd0, ifdExif) -> [ifd0 <> pure exifOffsetIfd, ifdExif]
-  
+
   exifBlob = runPut $ do
     putByteString $ BC.pack "Exif\0\0"
     putP BC.empty (hdr, ifdList)
@@ -525,48 +698,191 @@     putWord16be . fromIntegral $ L.length exifBlob + 2
     putLazyByteString exifBlob
 
-parseFrames :: Get [JpgFrame]
-parseFrames = do
-    kind <- get
-    let parseNextFrame = do
-            word <- getWord8
-            when (word /= 0xFF) $ do
-                readedData <- bytesRead
-                fail $ "Invalid Frame marker (" ++ show word
-                     ++ ", bytes read : " ++ show readedData ++ ")"
-            parseFrames
+skipFrameMarker :: Get ()
+skipFrameMarker = do
+    word <- getWord8
+    when (word /= 0xFF) $ do
+        readedData <- bytesRead
+        fail $ "Invalid Frame marker (" ++ show word
+                ++ ", bytes read : " ++ show readedData ++ ")"
 
+-- | Parses a single frame.
+--
+-- Returns `Nothing` when we encounter a frame we want to skip.
+parseFrameOfKind :: JpgFrameKind -> Get (Maybe JpgFrame)
+parseFrameOfKind kind = do
     case kind of
-        JpgEndOfImage -> return []
-        JpgAppSegment 0 ->
-            parseJF__ <$> takeCurrentFrame <*> parseNextFrame
-        JpgAppSegment 1 ->
-            parseExif <$> takeCurrentFrame <*> parseNextFrame
-        JpgAppSegment 14 ->
-            parseAdobe14 <$> takeCurrentFrame <*> parseNextFrame
-        JpgAppSegment c ->
-            (\frm lst -> JpgAppFrame c frm : lst) <$> takeCurrentFrame <*> parseNextFrame
-        JpgExtensionSegment c ->
-            (\frm lst -> JpgExtension c frm : lst) <$> takeCurrentFrame <*> parseNextFrame
+        JpgEndOfImage -> return Nothing
+        JpgAppSegment 0 -> parseJF__ <$> takeCurrentFrame
+        JpgAppSegment 1 -> parseExif <$> takeCurrentFrame
+        JpgAppSegment 14 -> parseAdobe14 <$> takeCurrentFrame
+        JpgAppSegment c -> Just . JpgAppFrame c <$> takeCurrentFrame
+        JpgExtensionSegment c -> Just . JpgExtension c <$> takeCurrentFrame
         JpgQuantizationTable ->
-            (\(TableList quants) lst -> JpgQuantTable quants : lst) <$> get <*> parseNextFrame
+            (\(TableList quants) -> Just $! JpgQuantTable quants) <$> get
         JpgRestartInterval ->
-            (\(RestartInterval i) lst -> JpgIntervalRestart i : lst) <$> get <*> parseNextFrame
+            (\(RestartInterval i) -> Just $! JpgIntervalRestart i) <$> get
         JpgHuffmanTableMarker ->
-            (\(TableList huffTables) lst ->
-                    JpgHuffmanTable [(t, packHuffmanTree . buildPackedHuffmanTree $ huffCodes t) | t <- huffTables] : lst)
-                    <$> get <*> parseNextFrame
-        JpgStartOfScan ->
-            (\frm imgData ->
-                let (d, other) = extractScanContent imgData
-                in
-                case runGet parseFrames (L.drop 1 other) of
-                  Left _ -> [JpgScanBlob frm d]
-                  Right lst -> JpgScanBlob frm d : lst
-            ) <$> get <*> getRemainingLazyBytes
+            (\(TableList huffTables) -> Just $!
+                    JpgHuffmanTable [(t, packHuffmanTree . buildPackedHuffmanTree $ huffCodes t) | t <- huffTables])
+                    <$> get
+        JpgStartOfScan -> do
+            scanHeader <- get
+            ecs <- parseECS
+            return $! Just $! JpgScanBlob scanHeader ecs
+        _ -> Just . JpgScans kind <$> get
 
-        _ -> (\hdr lst -> JpgScans kind hdr : lst) <$> get <*> parseNextFrame
 
+-- | Parse a list of `JpgFrame`s.
+--
+-- This function has various quirks; consider the below with great caution
+-- when using this function.
+--
+-- While @data JpgFrame = ... | JpgScanBlob !...` itself has strict fields,
+--
+-- This function is written in such a way that that it can construct
+-- the @[JpgFrame]@ "lazily" such that the expensive byte-by-byte traversal
+-- in `parseECS` to create a `JpgScanBlob` can be avoided if only
+-- list elements before that `JpgScanBlob` are evaluated.
+--
+-- That means the user can write code such as
+--
+-- > let mbFirstScan =
+-- >       case runGetOrFail (get @JPG.JpgImage) hugeImageByteString of -- (`get @JPG.JpgImage` uses `parseFramesSemiLazy`)
+-- >         Right (_restBs, _offset, res) ->
+-- >           find (\frame -> case frame of { JPG.JpgScans{} -> True; _ -> False }) (JPG.jpgFrame res)
+--
+-- with the guarantee that only the bytes before the ECS (large compressed image data)
+-- will be inspected, assuming that indeed there is at least 1 `JpgScan` in front
+-- of the `JpgScanBlob` that contains the ECS.
+--
+-- This guarantee can be useful to e.g. quickly read just the image
+-- dimensions (width, height) without traversing the large data.
+--
+-- Also note that this `Get` parser does not correctly maintain the parser byte offset
+-- (`Data.Binary.Get.bytesRead`), because as soon as a `JpgStartOfScan` is returned,
+-- it uses `Data.Binary.Get.getRemainingLazyBytes` to provide:
+--
+-- 1. the laziness described above, and
+-- 2. the ability to ignore any parser failure after the first successfully-parsed
+--    `JpgScanBlob` (it is debatable whether this behaviour is a desirable behaviour of this
+--    library, but it is historically so and existing exposed functions do not break
+--    this for backwards compatibility with existing uses of this library).
+--    This fact also means that even `parseNextFrameStrict` cannot maintain
+--    correct parser byte offsets.
+--
+-- Further note that if you are reading a huge JPEG image from disk strictly,
+-- this will already incur a full traversal (namely creation) of the `hugeImageByteString`.
+-- Thus, `parseNextFrameLazy` only provides any benefit if you:
+--
+-- - read the image from disk using lazy IO (not recommended!) such as via
+--   `Data.ByteString.Lazy.readFile`,
+-- - or do something similar, such as creating the `hugeImageByteString` via @mmap()@
+--
+-- This function is called "semi lazy" because only the first `JpgScanBlob` returned
+-- in the `[JpgFrame]` is returned lazily; frames of other types, or multiple
+-- `JpgScanBlob`s, are confusingly not dealt with lazily.
+--
+-- If as a caller you do not want to deal with any of these quirks,
+-- and use proper strict IO and/or via `Data.Binary.Get`'s incremental input interface:
+--
+-- - If you want the whole `[JpgFrame]`: use `parseFrames`.
+-- - If you want parsing to terminate early as in the example shown above,
+--   use in combination with just the right amount of `get :: Get JpgFrameKind`,
+--   `parseFrameOfKind`, and `skipFrameMarker`.
+parseFramesSemiLazy :: Get [JpgFrame]
+parseFramesSemiLazy = do
+    kind <- get
+    case kind of
+        -- The end-of-image case needs to be here because `_ ->` default case below
+        -- unconditionally uses `skipFrameMarker` which does not exist after `JpgEndOfImage`.
+        JpgEndOfImage -> pure []
+        JpgStartOfScan -> do
+            scanHeader <- get
+            remainingBytes <- getRemainingLazyBytes
+            -- It is after the above `getRemainingLazyBytes` that the `Get` parser lazily succeeds,
+            -- allowing consumers of `parseFramesSemiLazy` evaluate all `[JpgFrame]` list elements
+            -- until (excluding) the cons-cell around the `JpgScanBlob ...` we construct below.
+
+            return $ case runGet parseECS remainingBytes of
+                Left _ ->
+                    -- Construct invalid `JpgScanBlob` even when the compressed JPEG
+                    -- data is truncated or otherwise invalid, because that's what JuicyPixels's
+                    -- `parseFramesSemiLazy` function did in the past, for backwards compat.
+                    [JpgScanBlob scanHeader remainingBytes]
+                Right ecs ->
+                    JpgScanBlob scanHeader ecs
+                    :
+                    -- TODO Why `drop 1` instead of `runGet (skipFrameMarker *> parseFramesSemiLazy) remainingBytes` that would check that the dropped 1 Byte is really a frame marker?
+                    case runGet parseFramesSemiLazy (L.drop (L.length ecs + 1) remainingBytes) of
+                        -- After we've encountered the first scan blob containing encoded image data,
+                        -- we accept anything else after to fail parsing, ignoring that failure,
+                        -- end emitting no further frames.
+                        -- TODO: Explain why JuicyPixel chose to use this logic, insteaed of failing.
+                        Left _ -> []
+                        Right remainingFrames -> remainingFrames
+        _ -> do
+            mbFrame <- parseFrameOfKind kind
+            skipFrameMarker
+            remainingFrames <- parseFramesSemiLazy
+            return $ maybeToList mbFrame ++ remainingFrames
+
+-- | Parse a list of `JpgFrame`s.
+parseFrames :: Get [JpgFrame]
+parseFrames = do
+    kind <- get
+    case kind of
+        JpgEndOfImage -> pure []
+        _ -> do
+            mbFrame <- parseFrameOfKind kind
+            skipFrameMarker
+            remainingFrames <- parseFrames
+            return $ maybeToList mbFrame ++ remainingFrames
+
+-- | Parse a list of `JpgFrameKind`s with their corresponding offsets and lengths
+-- (not counting the segment and frame markers into the lengths).
+--
+-- Useful for debugging.
+parseFrameKinds :: Get [(JpgFrameKind, Int64, Int64)]
+parseFrameKinds = do
+    kindMarkerOffset :: Int64 <- bytesRead
+    kind <- get
+    case kind of
+        JpgEndOfImage -> pure [(JpgEndOfImage, kindMarkerOffset, 0)]
+        _ -> do
+            parserOffsetBefore <- bytesRead
+            _ <- parseFrameOfKind kind
+            parserOffsetAfter <- bytesRead
+            let !segmentLengthWithoutMarker = parserOffsetAfter - parserOffsetBefore
+            skipFrameMarker
+            remainingKinds <- parseFrameKinds
+            return $ (kind, kindMarkerOffset, segmentLengthWithoutMarker):remainingKinds
+
+-- | Parses forward, returning the first scan header encountered.
+--
+-- Should be used after `skipUntilFrames`.
+--
+-- Fails parsing when an SOS segment marker (`JpgStartOfScan`, resulting
+-- in `JpgScanBlob`) is encountered before an SOF segment marker (that
+-- results in `JpgScans` carrying the `JpgFrameHeader`).
+parseToFirstFrameHeader :: Get (Maybe JpgFrameHeader)
+parseToFirstFrameHeader = do
+    kind <- get
+    case kind of
+        JpgEndOfImage -> return Nothing
+        JpgStartOfScan -> fail "parseToFirstFrameHeader: Encountered SOS frame marker before frame header that tells its dimensions"
+        _ -> do
+            mbFrame <- parseFrameOfKind kind
+            case mbFrame of
+                Nothing -> continueSearching
+                Just frame -> case frame of
+                    JpgScans _ frameHeader -> return $ Just $! frameHeader
+                    _ -> continueSearching
+  where
+    continueSearching = do
+        skipFrameMarker
+        parseToFirstFrameHeader
+
 buildPackedHuffmanTree :: V.Vector (VU.Vector Word8) -> HuffmanTree
 buildPackedHuffmanTree = buildHuffmanTree . map VU.toList . V.toList
 
@@ -603,29 +919,29 @@         -- no lookahead :(
         {-word <- getWord8-}
         word2 <- getWord8
-        return $ case word2 of
-            0xC0 -> JpgBaselineDCTHuffman
-            0xC1 -> JpgExtendedSequentialDCTHuffman
-            0xC2 -> JpgProgressiveDCTHuffman
-            0xC3 -> JpgLosslessHuffman
-            0xC4 -> JpgHuffmanTableMarker
-            0xC5 -> JpgDifferentialSequentialDCTHuffman
-            0xC6 -> JpgDifferentialProgressiveDCTHuffman
-            0xC7 -> JpgDifferentialLosslessHuffman
-            0xC9 -> JpgExtendedSequentialArithmetic
-            0xCA -> JpgProgressiveDCTArithmetic
-            0xCB -> JpgLosslessArithmetic
-            0xCD -> JpgDifferentialSequentialDCTArithmetic
-            0xCE -> JpgDifferentialProgressiveDCTArithmetic
-            0xCF -> JpgDifferentialLosslessArithmetic
-            0xD9 -> JpgEndOfImage
-            0xDA -> JpgStartOfScan
-            0xDB -> JpgQuantizationTable
-            0xDD -> JpgRestartInterval
-            a | a >= 0xF0 -> JpgExtensionSegment a
-              | a >= 0xE0 -> JpgAppSegment (a - 0xE0)
-              | a >= 0xD0 && a <= 0xD7 -> JpgRestartIntervalEnd a
-              | otherwise -> error ("Invalid frame marker (" ++ show a ++ ")")
+        case word2 of
+            0xC0 -> return JpgBaselineDCTHuffman
+            0xC1 -> return JpgExtendedSequentialDCTHuffman
+            0xC2 -> return JpgProgressiveDCTHuffman
+            0xC3 -> return JpgLosslessHuffman
+            0xC4 -> return JpgHuffmanTableMarker
+            0xC5 -> return JpgDifferentialSequentialDCTHuffman
+            0xC6 -> return JpgDifferentialProgressiveDCTHuffman
+            0xC7 -> return JpgDifferentialLosslessHuffman
+            0xC9 -> return JpgExtendedSequentialArithmetic
+            0xCA -> return JpgProgressiveDCTArithmetic
+            0xCB -> return JpgLosslessArithmetic
+            0xCD -> return JpgDifferentialSequentialDCTArithmetic
+            0xCE -> return JpgDifferentialProgressiveDCTArithmetic
+            0xCF -> return JpgDifferentialLosslessArithmetic
+            0xD9 -> return JpgEndOfImage
+            0xDA -> return JpgStartOfScan
+            0xDB -> return JpgQuantizationTable
+            0xDD -> return JpgRestartInterval
+            a | a >= 0xF0 -> return $! JpgExtensionSegment a
+              | a >= 0xE0 -> return $! JpgAppSegment (a - 0xE0)
+              | a >= 0xD0 && a <= 0xD7 -> return $! JpgRestartIntervalEnd a
+              | otherwise -> fail ("Invalid frame marker (" ++ show a ++ ")")
 
 put4BitsOfEach :: Word8 -> Word8 -> Put
 put4BitsOfEach a b = put $ (a `unsafeShiftL` 4) .|. b
src/Codec/Picture/Metadata.hs view
@@ -43,6 +43,8 @@                              , dotsPerCentiMeterToDotPerInch
                              ) where
 
+import Prelude hiding (Foldable(..))
+
 #if !MIN_VERSION_base(4,8,0)
 import Data.Monoid( Monoid, mempty, mappend )
 import Data.Word( Word )
src/Codec/Picture/Metadata/Exif.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE DeriveGeneric #-}
+
 -- | This module provide a totally partial and incomplete maping
 -- of Exif values. Used for Tiff parsing and reused for Exif extraction.
 module Codec.Picture.Metadata.Exif ( ExifTag( .. )
@@ -14,6 +16,7 @@ import Data.Word( Word16, Word32 )
 import qualified Data.Vector as V
 import qualified Data.ByteString as B
+import GHC.Generics( Generic )
 
 -- | Tag values used for exif fields. Completly incomplete
 data ExifTag
@@ -72,7 +75,8 @@ 
   | TagExifOffset
   | TagUnknown !Word16
-  deriving (Eq, Ord, Show)
+  deriving (Eq, Ord, Show, Generic)
+instance NFData ExifTag
 
 -- | Convert a value to it's corresponding Exif tag.
 -- Will often be written as 'TagUnknown'
@@ -206,13 +210,5 @@   | ExifRational  !Word32 !Word32
   | ExifSignedRational  !Int32 !Int32
   | ExifIFD       ![(ExifTag, ExifData)]
-  deriving Show
-
-instance NFData ExifTag where
-  rnf a = a `seq` ()
-
-instance NFData ExifData where
-  rnf (ExifIFD ifds) = rnf ifds `seq` ()
-  rnf (ExifLongs l) = rnf l `seq` ()
-  rnf (ExifShorts l) = rnf l `seq` ()
-  rnf a = a `seq` ()
+  deriving (Eq, Show, Generic)
+instance NFData ExifData
src/Codec/Picture/Png.hs view
@@ -366,8 +366,8 @@ deinterlacer :: PngIHdr -> B.ByteString -> ST s (Either (V.Vector Word8) (V.Vector Word16))
 deinterlacer (PngIHdr { width = w, height = h, colourType  = imgKind
                       , interlaceMethod = method, bitDepth = depth  }) str = do
-    let compCount = sampleCountOfImageType imgKind 
-        arraySize = fromIntegral $ w * h * compCount
+    let compCount = fromIntegral $ sampleCountOfImageType imgKind 
+        arraySize = (fromIntegral w) * (fromIntegral h) * compCount
         deinterlaceFunction = case method of
             PngNoInterlace -> scanLineInterleaving
             PngInterlaceAdam7 -> adam7Unpack
@@ -377,10 +377,9 @@         imgArray <- M.new arraySize
         let mutableImage = MutableImage (fromIntegral w) (fromIntegral h) imgArray
         deinterlaceFunction iBitDepth 
-                            (fromIntegral compCount)
+                            compCount
                             (fromIntegral w, fromIntegral h)
-                            (scanlineUnpacker8 iBitDepth (fromIntegral compCount)
-                                                         mutableImage)
+                            (scanlineUnpacker8 iBitDepth compCount mutableImage)
                             str
         Left <$> V.unsafeFreeze imgArray
 
@@ -388,9 +387,9 @@         imgArray <- M.new arraySize
         let mutableImage = MutableImage (fromIntegral w) (fromIntegral h) imgArray
         deinterlaceFunction iBitDepth 
-                            (fromIntegral compCount)
+                            compCount
                             (fromIntegral w, fromIntegral h)
-                            (shortUnpacker (fromIntegral compCount) mutableImage)
+                            (shortUnpacker compCount mutableImage)
                             str
         Right <$> V.unsafeFreeze imgArray
 
src/Codec/Picture/Png/Internal/Export.hs view
@@ -93,7 +93,7 @@     --  * 'Codec.Picture.Metadata.Warning'
     --  * 'Codec.Picture.Metadata.Unknown' using the key present in the constructor.
     -- 
-    -- the followings metadata will bes tored in the `gAMA` chunk.
+    -- the followings metadata will be stored in the `gAMA` chunk.
     --
     --  * 'Codec.Picture.Metadata.Gamma'
     --
src/Codec/Picture/Png/Internal/Type.hs view
@@ -14,6 +14,8 @@                              , APngFrameControl( .. )
                              , parsePalette 
                              , pngComputeCrc
+                             , pngSignature
+                             , iHDRSignature
                              , pLTESignature
                              , iDATSignature
                              , iENDSignature
src/Codec/Picture/Tiff/Internal/Types.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FlexibleContexts #-}
@@ -32,6 +33,7 @@ import Control.Applicative( (<$>), (<*>), pure )
 #endif
 
+import Control.DeepSeq( NFData(..) )
 import Control.Monad( forM_, when, replicateM, )
 import Data.Bits( (.&.), unsafeShiftR )
 import Data.Binary( Binary( .. ) )
@@ -53,6 +55,7 @@ import qualified Data.ByteString as B
 import Data.Int( Int32 )
 import Data.Word( Word8, Word16, Word32 )
+import GHC.Generics( Generic )
 
 import Codec.Picture.Metadata.Exif
 {-import Debug.Trace-}
@@ -154,7 +157,8 @@   | TypeSignedRational
   | TypeFloat
   | TypeDouble
-  deriving Show
+  deriving (Eq, Show, Generic)
+instance NFData IfdType
 
 instance BinaryParam Endianness IfdType where
     getP endianness = getP endianness >>= conv where
@@ -240,6 +244,16 @@ 
       getVec count = V.replicateM (fromIntegral count)
 
+      immediateBytes ofs =
+        let bytes = [fromIntegral $ (ofs .&. 0xFF000000) `unsafeShiftR` (3 * 8)
+                    ,fromIntegral $ (ofs .&. 0x00FF0000) `unsafeShiftR` (2 * 8)
+                    ,fromIntegral $ (ofs .&. 0x0000FF00) `unsafeShiftR` (1 * 8)
+                    ,fromIntegral $  ofs .&. 0x000000FF
+                    ]
+        in case endianness of
+             EndianLittle -> reverse bytes
+             EndianBig    -> bytes
+
       fetcher ImageFileDirectory { ifdIdentifier = TagExifOffset
                                  , ifdType = TypeLong
                                  , ifdCount = 1 } = do
@@ -262,13 +276,12 @@          align ifd $ ExifUndefined <$> getByteString (fromIntegral count)
       fetcher ImageFileDirectory { ifdType = TypeUndefined, ifdOffset = ofs } =
           pure . ExifUndefined . B.pack $ take (fromIntegral $ ifdCount ifd)
-              [fromIntegral $ ofs .&. 0xFF000000 `unsafeShiftR` (3 * 8)
-              ,fromIntegral $ ofs .&. 0x00FF0000 `unsafeShiftR` (2 * 8)
-              ,fromIntegral $ ofs .&. 0x0000FF00 `unsafeShiftR` (1 * 8)
-              ,fromIntegral $ ofs .&. 0x000000FF
-              ]
-      fetcher ImageFileDirectory { ifdType = TypeAscii, ifdCount = count } | count > 1 =
+              (immediateBytes ofs)
+      fetcher ImageFileDirectory { ifdType = TypeAscii, ifdCount = count } | count > 4 =
           align ifd $ ExifString <$> getByteString (fromIntegral count)
+      fetcher ImageFileDirectory { ifdType = TypeAscii, ifdOffset = ofs } =
+          pure . ExifString . B.pack $ take (fromIntegral $ ifdCount ifd)
+              (immediateBytes ofs)
       fetcher ImageFileDirectory { ifdType = TypeShort, ifdCount = 2, ifdOffset = ofs } =
           pure . ExifShorts $ V.fromListN 2 valList
             where high = fromIntegral $ ofs `unsafeShiftR` 16
@@ -394,7 +407,8 @@   , ifdOffset     :: !Word32
   , ifdExtended   :: !ExifData
   }
-  deriving Show
+  deriving (Eq, Show, Generic)
+instance NFData ImageFileDirectory
 
 instance BinaryParam Endianness ImageFileDirectory where
   getP endianness =
src/Codec/Picture/VectorByteConversion.hs view
@@ -21,12 +21,19 @@ 
 import Codec.Picture.Types
 
+mkBS :: ForeignPtr Word8 -> Int -> Int -> S.ByteString
+#if MIN_VERSION_bytestring(0,11,0)
+mkBS fptr off = S.BS (fptr `S.plusForeignPtr` off)
+#else
+mkBS = S.PS
+#endif
+
 blitVector :: Vector Word8 -> Int -> Int -> B.ByteString
-blitVector vec atIndex = S.PS ptr (offset + atIndex)
+blitVector vec atIndex = mkBS ptr (offset + atIndex)
   where (ptr, offset, _length) = unsafeToForeignPtr vec
 
 toByteString :: forall a. (Storable a) => Vector a -> B.ByteString
-toByteString vec = S.PS (castForeignPtr ptr) offset (len * size)
+toByteString vec = mkBS (castForeignPtr ptr) offset (len * size)
   where (ptr, offset, len) = unsafeToForeignPtr vec
         size = sizeOf (undefined :: a)