Merge branch 'better-gl' into v2.15.x
authorCarl Hetherington <cth@carlh.net>
Mon, 27 Sep 2021 11:43:19 +0000 (13:43 +0200)
committerCarl Hetherington <cth@carlh.net>
Mon, 27 Sep 2021 11:43:19 +0000 (13:43 +0200)
This changes the GL video view to use more modern GL (GLSL etc.)  It
also special-cases JPEG2000 video playback and does scaling and
colourspace conversion on the GPU.

72 files changed:
doc/design/video.svg
hacks/gl-notes [new file with mode: 0644]
hacks/gl/modern.cc [new file with mode: 0644]
hacks/gl/shader.cc [new file with mode: 0644]
platform/windows/wscript
src/lib/bitmap_text.h
src/lib/butler.cc
src/lib/butler.h
src/lib/content_text.h
src/lib/dcp_video.cc
src/lib/encoder.cc
src/lib/exceptions.cc
src/lib/exceptions.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_encoder.cc
src/lib/ffmpeg_file_encoder.cc
src/lib/ffmpeg_image_proxy.cc
src/lib/ffmpeg_image_proxy.h
src/lib/hints.cc
src/lib/image.cc
src/lib/image.h
src/lib/image_examiner.cc
src/lib/image_proxy.h
src/lib/j2k_image_proxy.cc
src/lib/j2k_image_proxy.h
src/lib/player.cc
src/lib/player.h
src/lib/player_video.cc
src/lib/player_video.h
src/lib/position_image.h
src/lib/raw_image_proxy.cc
src/lib/raw_image_proxy.h
src/lib/render_text.cc
src/lib/text_decoder.cc
src/lib/text_decoder.h
src/lib/util.cc
src/lib/video_filter_graph.cc
src/tools/dcpomatic_player.cc
src/tools/server_test.cc
src/wx/controls.cc
src/wx/controls.h
src/wx/film_viewer.cc
src/wx/film_viewer.h
src/wx/gl_video_view.cc
src/wx/gl_video_view.h
src/wx/playlist_controls.cc
src/wx/simple_video_view.cc
src/wx/simple_video_view.h
src/wx/standard_controls.cc
src/wx/system_information_dialog.cc
src/wx/system_information_dialog.h
src/wx/video_view.cc
src/wx/video_view.h
src/wx/video_waveform_plot.cc
src/wx/wscript
test/butler_test.cc
test/client_server_test.cc
test/dcp_decoder_test.cc
test/dcp_playback_test.cc
test/ffmpeg_audio_only_test.cc
test/ffmpeg_audio_test.cc
test/ffmpeg_decoder_sequential_test.cc
test/image_test.cc
test/low_bitrate_test.cc
test/overlap_video_test.cc
test/pixel_formats_test.cc
test/player_test.cc
test/test.cc
test/time_calculation_test.cc
test/upmixer_a_test.cc
test/vf_test.cc
test/video_level_test.cc

index c9985b1b64f5ee7e719924e3b6c22818996512e9..59122b46fdcf4a46d40aaf9497902a65b18bd139 100644 (file)
@@ -1,6 +1,4 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
 <svg
    xmlns:dc="http://purl.org/dc/elements/1.1/"
    xmlns:cc="http://creativecommons.org/ns#"
    viewBox="0 0 744.09448819 1052.3622047"
    id="svg2"
    version="1.1"
-   inkscape:version="0.91 r13725"
+   inkscape:version="1.0.2 (e86c870, 2021-01-15)"
    sodipodi:docname="video.svg">
   <defs
      id="defs4">
+    <marker
+       style="overflow:visible;"
+       id="marker3668"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="scale(0.4) rotate(180) translate(10,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path3666" />
+    </marker>
+    <marker
+       style="overflow:visible;"
+       id="marker2973"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="scale(0.4) rotate(180) translate(10,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path2971" />
+    </marker>
+    <marker
+       style="overflow:visible;"
+       id="marker1688"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true"
+       inkscape:collect="always">
+      <path
+         transform="scale(0.4) rotate(180) translate(10,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path1434" />
+    </marker>
     <marker
        inkscape:isstock="true"
        style="overflow:visible;"
          d="M 0,0 5,-5 -12.5,0 5,5 0,0 Z"
          id="path8484-9" />
     </marker>
+    <marker
+       style="overflow:visible"
+       id="marker1688-3"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(-0.4,0,0,-0.4,-4,0)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path1434-7" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker1688-3-2"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(-0.4,0,0,-0.4,-4,0)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path1434-7-8" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker1688-3-29"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(-0.4,0,0,-0.4,-4,0)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path1434-7-3" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker1688-3-29-9"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(-0.4,0,0,-0.4,-4,0)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path1434-7-3-4" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker3668-5"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(-0.4,0,0,-0.4,-4,0)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path3666-6" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker1688-3-29-9-2"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(-0.4,0,0,-0.4,-4,0)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path1434-7-3-4-5" />
+    </marker>
+    <marker
+       style="overflow:visible"
+       id="marker1688-3-29-9-2-7"
+       refX="0"
+       refY="0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mend"
+       inkscape:isstock="true">
+      <path
+         transform="matrix(-0.4,0,0,-0.4,-4,0)"
+         style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1"
+         d="M 0,0 5,-5 -12.5,0 5,5 Z"
+         id="path1434-7-3-4-5-4" />
+    </marker>
   </defs>
   <sodipodi:namedview
      id="base"
      borderopacity="1.0"
      inkscape:pageopacity="0.0"
      inkscape:pageshadow="2"
-     inkscape:zoom="12.895211"
-     inkscape:cx="-38.958419"
-     inkscape:cy="710.99114"
+     inkscape:zoom="1.6119014"
+     inkscape:cx="436.93023"
+     inkscape:cy="615.356"
      inkscape:document-units="px"
      inkscape:current-layer="layer1"
      showgrid="false"
      showguides="true"
      inkscape:guide-bbox="true"
-     inkscape:window-width="1280"
-     inkscape:window-height="995"
-     inkscape:window-x="86"
-     inkscape:window-y="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="1016"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
      inkscape:window-maximized="1"
      inkscape:showpageshadow="true"
      showborder="false"
-     inkscape:snap-global="false" />
+     inkscape:snap-global="false"
+     inkscape:document-rotation="0" />
   <metadata
      id="metadata7">
     <rdf:RDF>
         <dc:format>image/svg+xml</dc:format>
         <dc:type
            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
+        <dc:title />
       </cc:Work>
     </rdf:RDF>
   </metadata>
        sodipodi:nodetypes="cccc" />
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="157.97063"
        y="145.07965"
-       id="text3355-3"
-       sodipodi:linespacing="125%"><tspan
+       id="text3355-3"><tspan
          sodipodi:role="line"
          id="tspan3357-6"
          x="157.97063"
-         y="145.07965">Player</tspan></text>
+         y="145.07965"
+         style="font-size:14px;line-height:1.25">Player</tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="261.11292"
        y="79.787865"
-       id="text3405"
-       sodipodi:linespacing="125%"><tspan
+       id="text3405"><tspan
          sodipodi:role="line"
          id="tspan3407"
          x="261.11292"
-         y="79.787865">Call <tspan
+         y="79.787865"
+         style="font-size:8px;line-height:1.25">Call <tspan
    style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';fill:#ff0000"
    id="tspan3425">give(<tspan
    style="fill:#0000ff"
    id="tspan4943">ImageProxy</tspan>)</tspan></tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="158.08263"
        y="27.967474"
-       id="text3459"
-       sodipodi:linespacing="125%"><tspan
+       id="text3459"><tspan
          sodipodi:role="line"
          id="tspan3461"
          x="158.08263"
-         y="27.967474">FFmpegDecoder</tspan><tspan
+         y="27.967474"
+         style="font-size:14px;line-height:1.25">FFmpegDecoder</tspan><tspan
          sodipodi:role="line"
          x="158.08263"
          y="45.467476"
-         id="tspan3463">DCPDecoder</tspan><tspan
+         id="tspan3463"
+         style="font-size:14px;line-height:1.25">DCPDecoder</tspan><tspan
          sodipodi:role="line"
          x="158.08263"
          y="62.967476"
-         id="tspan3465">VideoMXFDecoder</tspan><tspan
+         id="tspan3465"
+         style="font-size:14px;line-height:1.25">VideoMXFDecoder</tspan><tspan
          sodipodi:role="line"
          x="158.08263"
          y="80.467476"
-         id="tspan3467">ImageDecoder</tspan></text>
+         id="tspan3467"
+         style="font-size:14px;line-height:1.25">ImageDecoder</tspan></text>
     <text
        xml:space="preserve"
-       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
        x="750.14496"
        y="30.933632"
-       id="text3503"
-       sodipodi:linespacing="125%"><tspan
+       id="text3503"><tspan
          sodipodi:role="line"
          id="tspan3505"
          x="750.14496"
-         y="30.933632" /></text>
+         y="30.933632"
+         style="font-size:40px;line-height:1.25"> </tspan></text>
     <text
        xml:space="preserve"
-       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
        x="730.14496"
        y="42.362206"
-       id="text3507"
-       sodipodi:linespacing="125%"><tspan
+       id="text3507"><tspan
          sodipodi:role="line"
          id="tspan3509"
          x="730.14496"
-         y="42.362206" /></text>
+         y="42.362206"
+         style="font-size:40px;line-height:1.25"> </tspan></text>
     <text
        xml:space="preserve"
-       style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
        x="730.14496"
        y="48.076488"
-       id="text3511"
-       sodipodi:linespacing="125%"><tspan
+       id="text3511"><tspan
          sodipodi:role="line"
          id="tspan3513"
          x="730.14496"
-         y="48.076488" /></text>
+         y="48.076488"
+         style="font-size:40px;line-height:1.25"> </tspan></text>
     <g
        id="g6532"
        transform="translate(1.07113,-2.4356439)">
       <text
-         sodipodi:linespacing="125%"
          id="text3427"
          y="32.95697"
          x="365.3392"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            id="tspan3431"
            y="32.95697"
            x="365.3392"
-           sodipodi:role="line">RawImageProxy</tspan><tspan
+           sodipodi:role="line"
+           style="font-size:14px;line-height:1.25">RawImageProxy</tspan><tspan
            id="tspan3495"
            y="50.45697"
            x="365.3392"
-           sodipodi:role="line">MagickImageProxy</tspan><tspan
+           sodipodi:role="line"
+           style="font-size:14px;line-height:1.25">MagickImageProxy</tspan><tspan
            id="tspan3497"
            y="67.95697"
            x="365.3392"
-           sodipodi:role="line">J2KImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:14px;line-height:1.25">J2KImageProxy</tspan></text>
       <text
-         sodipodi:linespacing="125%"
          id="text3515"
          y="32.793625"
          x="468.0589"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="32.793625"
            x="468.0589"
            id="tspan3517"
-           sodipodi:role="line">image in any pixel format and colourspace</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">image in any pixel format and colourspace</tspan></text>
       <text
-         sodipodi:linespacing="125%"
          id="text3519"
          y="50.478642"
          x="489.33072"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="50.478642"
            x="489.33072"
            id="tspan3521"
-           sodipodi:role="line">image assumed to be 24-bit RGB</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">image assumed to be 24-bit RGB</tspan></text>
       <text
-         sodipodi:linespacing="125%"
          id="text3523"
          y="68.05394"
          x="468.04465"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="68.05394"
            x="468.04465"
            id="tspan3525"
-           sodipodi:role="line">image in J2K which could be sRGB or XYZ</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">image in J2K which could be sRGB or XYZ</tspan></text>
     </g>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="158.96458"
        y="114.28626"
-       id="text3598"
-       sodipodi:linespacing="125%"><tspan
+       id="text3598"><tspan
          sodipodi:role="line"
          id="tspan3600"
          x="158.96458"
-         y="114.28626">VideoDecoder</tspan></text>
+         y="114.28626"
+         style="font-size:14px;line-height:1.25">VideoDecoder</tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="210.7205"
        y="145.7095"
-       id="text4752"
-       sodipodi:linespacing="125%"><tspan
+       id="text4752"><tspan
          sodipodi:role="line"
          id="tspan4754"
          x="210.7205"
          y="145.7095"
-         style="font-size:8px">Add <tspan
-   style="fill:#0000ff;-inkscape-font-specification:'latin modern mono';font-family:'latin modern mono';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
+         style="font-size:8px;line-height:1.25">Add <tspan
+   style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';fill:#0000ff"
    id="tspan4945">ColourConversion</tspan> from Content</tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="232.61076"
        y="210.91785"
-       id="text4774"
-       sodipodi:linespacing="125%"><tspan
+       id="text4774"><tspan
          sodipodi:role="line"
          id="tspan4776"
          x="232.61076"
-         y="210.91785">Encoder</tspan></text>
+         y="210.91785"
+         style="font-size:14px;line-height:1.25">Encoder</tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="80.230049"
        y="210.91785"
-       id="text4778"
-       sodipodi:linespacing="125%"><tspan
+       id="text4778"><tspan
          sodipodi:role="line"
          id="tspan4780"
          x="80.230049"
-         y="210.91785">FilmViewer</tspan></text>
+         y="210.91785"
+         style="font-size:14px;line-height:1.25">FilmViewer</tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="80.358047"
        y="223.85684"
-       id="text4853"
-       sodipodi:linespacing="125%"><tspan
+       id="text4853"><tspan
          sodipodi:role="line"
          id="tspan4855"
          x="80.358047"
-         y="223.85684">Call <tspan
+         y="223.85684"
+         style="font-size:8px;line-height:1.25">Call <tspan
    style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono'"
    id="tspan4949"><tspan
    style="fill:#0000ff"
    id="tspan4953">image</tspan></tspan> telling</tspan><tspan
          sodipodi:role="line"
          x="80.358047"
-         y="234.18263"
-         id="tspan4857">FFmpeg to convert to RGB</tspan></text>
+         y="234.08084"
+         id="tspan4857"
+         style="font-size:8px;line-height:1.25">FFmpeg to convert to RGB</tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="232.70876"
        y="236.76064"
-       id="text4774-3"
-       sodipodi:linespacing="125%"><tspan
+       id="text4774-3"><tspan
          sodipodi:role="line"
          id="tspan4776-1"
          x="232.70876"
-         y="236.76064">DCPVideo</tspan><tspan
+         y="236.76064"
+         style="font-size:14px;line-height:1.25">DCPVideo</tspan><tspan
          sodipodi:role="line"
          x="232.70876"
          y="254.26064"
-         id="tspan4893" /></text>
+         id="tspan4893"
+         style="font-size:14px;line-height:1.25"> </tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="232.97478"
        y="246.51703"
-       id="text4895"
-       sodipodi:linespacing="125%"><tspan
+       id="text4895"><tspan
          sodipodi:role="line"
          x="232.97478"
          y="246.51703"
-         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono'"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:1.25;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono'"
          id="tspan4931"><tspan
            id="tspan8152"
            style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';fill:#ff0000">encode_locally()</tspan></tspan></text>
          id="rect7578"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.0292846px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580"
          y="173.77292"
          x="385.19406"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="173.77292"
            x="385.19406"
            id="tspan7582"
-           sodipodi:role="line">RawImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">RawImageProxy</tspan></text>
       <g
          transform="translate(-68.230852,57.45182)"
          id="g7700">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604"><tspan
              sodipodi:role="line"
              id="tspan7606"
              x="471.76764"
-             y="126.86294">YUV</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">YUV</tspan></text>
       </g>
     </g>
     <g
          id="rect7578-0"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.0292846px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-1"
          y="173.77292"
          x="385.19406"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="173.77292"
            x="385.19406"
            id="tspan7582-3"
-           sodipodi:role="line">RawImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">RawImageProxy</tspan></text>
       <g
          transform="translate(-68.230852,57.45182)"
          id="g7700-7">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-2"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-2"><tspan
              sodipodi:role="line"
              id="tspan7606-64"
              x="471.76764"
-             y="126.86294">RGB</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">RGB</tspan></text>
       </g>
     </g>
     <g
          id="rect7578-0-2"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.1262238px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-1-0"
          y="211.11105"
          x="453.9198"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="211.11105"
            x="453.9198"
            id="tspan7582-3-2"
-           sodipodi:role="line">MagickImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">MagickImageProxy</tspan></text>
       <g
          transform="translate(6.5830841,94.789948)"
          id="g7700-7-9">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-2-9"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-2-9"><tspan
              sodipodi:role="line"
              id="tspan7606-64-9"
              x="471.76764"
-             y="126.86294">RGB</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">RGB</tspan></text>
       </g>
     </g>
     <g
          id="rect7578-0-2-5"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.0356878px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-1-0-1"
          y="239.25938"
          x="404.95599"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="239.25938"
            x="404.95599"
            id="tspan7582-3-2-0"
-           sodipodi:role="line">J2KImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">J2KImageProxy</tspan></text>
       <g
          transform="translate(-48.084616,122.93827)"
          id="g7700-7-9-3">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-2-9-8"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-2-9-8"><tspan
              sodipodi:role="line"
              id="tspan7606-64-9-8"
              x="471.76764"
-             y="126.86294">RGB</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">RGB</tspan></text>
       </g>
     </g>
     <g
          id="rect7578-0-2-5-0"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.0356878px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-1-0-1-4"
          y="239.25938"
          x="404.95599"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="239.25938"
            x="404.95599"
            id="tspan7582-3-2-0-6"
-           sodipodi:role="line">J2KImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">J2KImageProxy</tspan></text>
       <g
          transform="translate(-48.084616,122.93827)"
          id="g7700-7-9-3-7">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-2-9-8-0"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-2-9-8-0"><tspan
              sodipodi:role="line"
              id="tspan7606-64-9-8-9"
              x="471.76764"
-             y="126.86294">XYZ</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">XYZ</tspan></text>
       </g>
     </g>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="705.09467"
        y="244.12233"
-       id="text8237"
-       sodipodi:linespacing="125%"><tspan
+       id="text8237"><tspan
          sodipodi:role="line"
          id="tspan8239"
          x="705.09467"
          y="244.12233"
-         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono'"><tspan
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:1.25;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono'"><tspan
    style="fill:#0000ff"
    id="tspan8257">Player</tspan>::<tspan
    style="fill:#ff0000"
        y="264.45612" />
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="705.1507"
        y="279.21188"
-       id="text8400"
-       sodipodi:linespacing="125%"><tspan
+       id="text8400"><tspan
          sodipodi:role="line"
          id="tspan8402"
          x="705.1507"
-         y="279.21188"><tspan
+         y="279.21188"
+         style="font-size:8px;line-height:1.25"><tspan
    style="fill:#0000ff"
    id="tspan8428">DCP</tspan>::<tspan
    style="fill:#ff0000"
          id="rect8285"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#eeaaff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text8154"
          y="244.55614"
          x="348.75104"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="244.55614"
            x="348.75104"
            id="tspan8156"
-           sodipodi:role="line">To RGB48LE</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">To RGB48LE</tspan></text>
     </g>
     <path
        style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker8482-3)"
        inkscape:connector-curvature="0" />
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="51.75613"
        y="226.5392"
        id="text9567"
-       sodipodi:linespacing="125%"
-       transform="matrix(0.88644001,-0.4628435,0.4628435,0.88644001,0,0)"><tspan
+       transform="rotate(-27.570746)"><tspan
          sodipodi:role="line"
          id="tspan9569"
          x="51.75613"
-         y="226.5392">Preview</tspan></text>
+         y="226.5392"
+         style="font-size:8px;line-height:1.25">Preview</tspan></text>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="277.17169"
        y="36.546879"
        id="text9571"
-       sodipodi:linespacing="125%"
-       transform="matrix(0.87866652,0.47743601,-0.47743601,0.87866652,0,0)"><tspan
+       transform="rotate(28.518077)"><tspan
          sodipodi:role="line"
          id="tspan9573"
          x="277.17169"
-         y="36.546879">Encode</tspan></text>
+         y="36.546879"
+         style="font-size:8px;line-height:1.25">Encode</tspan></text>
     <g
        id="g9672">
       <g
            y="235.07904" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="348.75104"
            y="244.55614"
-           id="text8154-0"
-           sodipodi:linespacing="125%"><tspan
+           id="text8154-0"><tspan
              sodipodi:role="line"
              id="tspan8156-4"
              x="348.75104"
-             y="244.55614">To XYZ12LE</tspan></text>
+             y="244.55614"
+             style="font-size:8px;line-height:1.25">To XYZ12LE</tspan></text>
       </g>
     </g>
     <g
            y="235.07904" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="348.75104"
            y="244.55614"
-           id="text8154-0-1"
-           sodipodi:linespacing="125%"><tspan
+           id="text8154-0-1"><tspan
              sodipodi:role="line"
              id="tspan8156-4-6"
              x="348.75104"
-             y="244.55614">To XYZ12LE</tspan></text>
+             y="244.55614"
+             style="font-size:8px;line-height:1.25">To XYZ12LE</tspan></text>
       </g>
     </g>
     <g
            y="235.07904" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="348.75104"
            y="244.55614"
-           id="text8154-0-9"
-           sodipodi:linespacing="125%"><tspan
+           id="text8154-0-9"><tspan
              sodipodi:role="line"
              id="tspan8156-4-64"
              x="348.75104"
-             y="244.55614">To XYZ12LE</tspan></text>
+             y="244.55614"
+             style="font-size:8px;line-height:1.25">To XYZ12LE</tspan></text>
       </g>
     </g>
     <g
            y="235.07904" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="348.75104"
            y="244.55614"
-           id="text8154-0-10"
-           sodipodi:linespacing="125%"><tspan
+           id="text8154-0-10"><tspan
              sodipodi:role="line"
              id="tspan8156-4-2"
              x="348.75104"
-             y="244.55614">To XYZ12LE</tspan></text>
+             y="244.55614"
+             style="font-size:8px;line-height:1.25">To XYZ12LE</tspan></text>
       </g>
     </g>
     <rect
          id="rect7578-9"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.0292846px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-17"
          y="173.77292"
          x="385.19406"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="173.77292"
            x="385.19406"
            id="tspan7582-0"
-           sodipodi:role="line">RawImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">RawImageProxy</tspan></text>
       <g
          transform="translate(-68.230852,57.45182)"
          id="g7700-6">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-8"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-8"><tspan
              sodipodi:role="line"
              id="tspan7606-5"
              x="471.76764"
-             y="126.86294">YUV</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">YUV</tspan></text>
       </g>
     </g>
     <g
          id="rect7578-0-9"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.0292846px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-1-4"
          y="173.77292"
          x="385.19406"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="173.77292"
            x="385.19406"
            id="tspan7582-3-1"
-           sodipodi:role="line">RawImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">RawImageProxy</tspan></text>
       <g
          transform="translate(-68.230852,57.45182)"
          id="g7700-7-5">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-2-1"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-2-1"><tspan
              sodipodi:role="line"
              id="tspan7606-64-5"
              x="471.76764"
-             y="126.86294">RGB</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">RGB</tspan></text>
       </g>
     </g>
     <g
          id="rect7578-0-2-4"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.1262238px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-1-0-9"
          y="211.11105"
          x="453.9198"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="211.11105"
            x="453.9198"
            id="tspan7582-3-2-8"
-           sodipodi:role="line">MagickImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">MagickImageProxy</tspan></text>
       <g
          transform="translate(6.5830841,94.789948)"
          id="g7700-7-9-38">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-2-9-2"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-2-9-2"><tspan
              sodipodi:role="line"
              id="tspan7606-64-9-2"
              x="471.76764"
-             y="126.86294">RGB</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">RGB</tspan></text>
       </g>
     </g>
     <g
          id="rect7578-0-2-5-7"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.0356878px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-1-0-1-0"
          y="239.25938"
          x="404.95599"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="239.25938"
            x="404.95599"
            id="tspan7582-3-2-0-3"
-           sodipodi:role="line">J2KImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">J2KImageProxy</tspan></text>
       <g
          transform="translate(-48.084616,122.93827)"
          id="g7700-7-9-3-4">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-2-9-8-6"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-2-9-8-6"><tspan
              sodipodi:role="line"
              id="tspan7606-64-9-8-3"
              x="471.76764"
-             y="126.86294">RGB</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">RGB</tspan></text>
       </g>
     </g>
     <g
          id="rect7578-0-2-5-0-4"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:#1a1a1a;stroke-width:1.0356878px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text7580-1-0-1-4-4"
          y="239.25938"
          x="404.95599"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="239.25938"
            x="404.95599"
            id="tspan7582-3-2-0-6-3"
-           sodipodi:role="line">J2KImageProxy</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">J2KImageProxy</tspan></text>
       <g
          transform="translate(-48.084616,122.93827)"
          id="g7700-7-9-3-7-9">
            y="118.93896" />
         <text
            xml:space="preserve"
-           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+           style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
            x="471.76764"
            y="126.86294"
-           id="text7604-2-9-8-0-2"
-           sodipodi:linespacing="125%"><tspan
+           id="text7604-2-9-8-0-2"><tspan
              sodipodi:role="line"
              id="tspan7606-64-9-8-9-5"
              x="471.76764"
-             y="126.86294">XYZ</tspan></text>
+             y="126.86294"
+             style="font-size:8px;line-height:1.25">XYZ</tspan></text>
       </g>
     </g>
     <text
        xml:space="preserve"
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
        x="5.1282115"
        y="332.96237"
-       id="text8237-8"
-       sodipodi:linespacing="125%"><tspan
+       id="text8237-8"><tspan
          sodipodi:role="line"
          id="tspan8239-9"
          x="5.1282115"
          y="332.96237"
-         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono'"><tspan
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:1.25;font-family:'latin modern mono';-inkscape-font-specification:'latin modern mono'"><tspan
    style="fill:#0000ff"
    id="tspan8257-0">Player</tspan>::<tspan
    style="fill:#ff0000"
          id="rect8285-5"
          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#eeaaff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
       <text
-         sodipodi:linespacing="125%"
          id="text8154-1"
          y="244.55614"
          x="354.7998"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:8px;line-height:125%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0%;font-family:'latin modern roman';-inkscape-font-specification:'latin modern roman, ';text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
          xml:space="preserve"><tspan
            y="244.55614"
            x="354.7998"
            id="tspan8156-0"
-           sodipodi:role="line">To RGB24</tspan></text>
+           sodipodi:role="line"
+           style="font-size:8px;line-height:1.25">To RGB24</tspan></text>
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+       x="-309.2775"
+       y="554.51569"
+       id="text1175"
+       transform="translate(32.203591,-61.256847)"><tspan
+         sodipodi:role="line"
+         id="tspan1173"
+         x="-309.2775"
+         y="554.51569"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">Decoder</tspan></text>
+    <g
+       id="g1275"
+       transform="translate(11.680627,-39.263855)">
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         x="-215.04077"
+         y="510.54083"
+         id="text1198"
+         transform="translate(-17.813399,3.8499265)"><tspan
+           sodipodi:role="line"
+           id="tspan1196"
+           x="-215.04077"
+           y="510.54083"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">ContentVideo</tspan><tspan
+           sodipodi:role="line"
+           x="-215.04077"
+           y="528.04083"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+           id="tspan1200">  </tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         x="-160.62468"
+         y="559.99548"
+         id="text1211"
+         transform="translate(-64.169864,-34.683152)"><tspan
+           sodipodi:role="line"
+           id="tspan1209"
+           x="-160.62468"
+           y="559.99548"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">ImageProxy</tspan><tspan
+           sodipodi:role="line"
+           x="-160.62468"
+           y="572.49548"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+           id="tspan1213" /><tspan
+           sodipodi:role="line"
+           x="-160.62468"
+           y="584.99548"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+           id="tspan1217" /><tspan
+           sodipodi:role="line"
+           x="-160.62468"
+           y="597.49548"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+           id="tspan1219" /></text>
     </g>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-309.2775"
+       y="554.51569"
+       id="text1175-2"
+       transform="translate(173.8463,-61.256847)"><tspan
+         sodipodi:role="line"
+         id="tspan1173-9"
+         x="-309.2775"
+         y="554.51569"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">Player</tspan></text>
+    <g
+       id="g1275-1"
+       transform="translate(149.15963,-38.395575)">
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         x="-215.04077"
+         y="510.54083"
+         id="text1198-2"
+         transform="translate(-17.813399,3.8499265)"><tspan
+           sodipodi:role="line"
+           id="tspan1196-7"
+           x="-215.04077"
+           y="510.54083"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">PlayerVideo</tspan><tspan
+           sodipodi:role="line"
+           x="-215.04077"
+           y="528.04083"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+           id="tspan1200-0">  </tspan></text>
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         x="-160.62468"
+         y="559.99548"
+         id="text1211-9"
+         transform="translate(-64.169864,-34.683152)"><tspan
+           sodipodi:role="line"
+           id="tspan1209-3"
+           x="-160.62468"
+           y="559.99548"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">ImageProxy</tspan><tspan
+           sodipodi:role="line"
+           x="-160.62468"
+           y="572.49548"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+           id="tspan1217-6" /><tspan
+           sodipodi:role="line"
+           x="-160.62468"
+           y="584.99548"
+           style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#008080;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+           id="tspan1219-2" /></text>
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-309.2775"
+       y="554.51569"
+       id="text1175-2-6"
+       transform="translate(312.02999,-57.755915)"><tspan
+         sodipodi:role="line"
+         id="tspan1173-9-1"
+         x="-309.2775"
+         y="554.51569"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-309.2775"
+       y="554.51569"
+       id="text1175-2-8"
+       transform="translate(308.1405,-61.256847)"><tspan
+         sodipodi:role="line"
+         id="tspan1173-9-7"
+         x="-309.2775"
+         y="554.51569"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">Butler</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+       x="-87.686157"
+       y="420.73074"
+       id="text1390"
+       transform="translate(4.0571512,20.445338)"><tspan
+         sodipodi:role="line"
+         id="tspan1388"
+         x="-87.686157"
+         y="420.73074"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">DCPEncoder</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker1688)"
+       d="m -111.77743,479.80479 43.572482,-33.42047"
+       id="path1411" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-87.686157"
+       y="420.73074"
+       id="text1390-0"
+       transform="translate(164.6192,124.17483)"><tspan
+         sodipodi:role="line"
+         id="tspan1388-2"
+         x="-87.686157"
+         y="420.73074"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">FFmpegEncoder</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker1688-3)"
+       d="M 33.640882,498.46273 77.213364,531.8832"
+       id="path1411-5" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-87.686157"
+       y="420.73074"
+       id="text1390-0-9"
+       transform="translate(167.72734,29.633718)"><tspan
+         sodipodi:role="line"
+         id="tspan1388-2-2"
+         x="-87.686157"
+         y="420.73074"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">VideoView</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker1688-3-2)"
+       d="M 34.296475,481.15447 77.868957,447.734"
+       id="path1411-5-9" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-87.686157"
+       y="420.73074"
+       id="text1390-0-9-7"
+       transform="translate(265.11469,-6.3252721)"><tspan
+         sodipodi:role="line"
+         id="tspan1388-2-2-3"
+         x="-87.686157"
+         y="420.73074"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">SimpleVideoView</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-87.686157"
+       y="420.73074"
+       id="text1390-0-9-6"
+       transform="translate(265.31069,64.227674)"><tspan
+         sodipodi:role="line"
+         id="tspan1388-2-2-1"
+         x="-87.686157"
+         y="420.73074"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">GLVideoView</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker1688-3-29)"
+       d="m 147.31102,448.44631 27.54102,32.33461"
+       id="path1411-5-1"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker1688-3-29-9)"
+       d="m 147.83317,443.8501 27.54102,-32.33461"
+       id="path1411-5-1-7"
+       sodipodi:nodetypes="cc" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+       x="336.05344"
+       y="420.61581"
+       id="text2864"
+       transform="translate(-31.120846,-23.390024)"><tspan
+         sodipodi:role="line"
+         id="tspan2862"
+         x="336.05344"
+         y="420.61581"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">calls PlayerVideo::image(), hence make_image() which</tspan><tspan
+         sodipodi:role="line"
+         x="336.05344"
+         y="438.11581"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         id="tspan2885">does crop_scale_window(), so the PlayerVideo's </tspan><tspan
+         sodipodi:role="line"
+         x="336.05344"
+         y="455.61581"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         id="tspan2887">ImageProxy must be PADDED.</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="336.05344"
+       y="420.61581"
+       id="text2864-3"
+       transform="translate(2.3793434,115.28813)"><tspan
+         sodipodi:role="line"
+         id="tspan2862-6"
+         x="336.05344"
+         y="420.61581"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">calls PlayerVideo::raw_image(), and passes</tspan><tspan
+         sodipodi:role="line"
+         x="336.05344"
+         y="438.11581"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         id="tspan2957">the result to GL, so the PlayerVideo's</tspan><tspan
+         sodipodi:role="line"
+         x="336.05344"
+         y="455.61581"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         id="tspan2959">ImageProxy must be COMPACT.</tspan><tspan
+         sodipodi:role="line"
+         x="336.05344"
+         y="473.11581"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         id="tspan2887-0" /></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+       x="309.21796"
+       y="618.7019"
+       id="text3143"
+       transform="translate(5.5245543,46.014145)"><tspan
+         sodipodi:role="line"
+         x="309.21796"
+         y="618.7019"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         id="tspan3145">[ImageProxy's image may be made</tspan><tspan
+         sodipodi:role="line"
+         x="309.21796"
+         y="636.2019"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         id="tspan3149">by ImageProxy::prepare() or by the</tspan><tspan
+         sodipodi:role="line"
+         x="309.21796"
+         y="653.7019"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+         id="tspan4786">decoder which made the ImageProxy]</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-variation-settings:normal;opacity:1;vector-effect:none;fill-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker-end:url(#marker3668);stop-color:#000000;stop-opacity:1"
+       d="m -219.14841,490.17833 h 79.16702"
+       id="path3184" />
+    <path
+       style="font-variation-settings:normal;vector-effect:none;fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#marker3668-5);stop-color:#000000"
+       d="M -85.603289,490.17833 H -6.4362693"
+       id="path3184-9" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-87.686157"
+       y="420.73074"
+       id="text1390-0-9-6-3"
+       transform="translate(395.74642,92.267467)"><tspan
+         sodipodi:role="line"
+         id="tspan1388-2-2-1-7"
+         x="-87.686157"
+         y="420.73074"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">[J2K optimised]</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
+       x="-87.686157"
+       y="420.73074"
+       id="text1390-0-9-6-3-4"
+       transform="translate(395.74227,43.24724)"><tspan
+         sodipodi:role="line"
+         id="tspan1388-2-2-1-7-5"
+         x="-87.686157"
+         y="420.73074"
+         style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14px;line-height:1.25;font-family:'Latin Modern Mono';-inkscape-font-specification:'Latin Modern Mono';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1">[supporting any source]</tspan></text>
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker1688-3-29-9-2)"
+       d="m 264.09526,481.80247 43.33723,-22.7705"
+       id="path1411-5-1-7-4"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:0.9375px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker1688-3-29-9-2-7)"
+       d="m 263.91609,485.60031 43.33723,22.7705"
+       id="path1411-5-1-7-4-4"
+       sodipodi:nodetypes="cc" />
   </g>
 </svg>
diff --git a/hacks/gl-notes b/hacks/gl-notes
new file mode 100644 (file)
index 0000000..0b1b11c
--- /dev/null
@@ -0,0 +1,17 @@
+4.3 with compute shaders "came out in 2012"
+Anything before 3.0 is deprecated (especially anything with glBegin/glEnd)
+
+https://stackoverflow.com/questions/14300569/opengl-glbegin-glend
+"The direct mode API with glBegin and glEnd is deprecated, largely for performance reasons."
+"Using VBOs scales better"
+
+OpenGL 3.3 tutorials (written 2012)
+http://www.opengl-tutorial.org/
+
+An intro to modern OpenGL (written 2010)
+https://duriansoftware.com/joe/an-intro-to-modern-opengl.-table-of-contents
+
+Learning Modern 3D Graphics Programming (written 2012)
+https://web.archive.org/web/20140209181347/http://www.arcsynthesis.org/gltut/
+
+"Since OpenGL3.3 most old stuff was moved to compatibility profile, while modern features are core profile; the latter requires self-written shaders, but they should be pretty simple for your case).
diff --git a/hacks/gl/modern.cc b/hacks/gl/modern.cc
new file mode 100644 (file)
index 0000000..748547e
--- /dev/null
@@ -0,0 +1,319 @@
+#include <QtGui/QGuiApplication>
+#include <QtGui/QKeyEvent>
+#include <QtGui/QOpenGLWindow>
+#include <QtGui/QOpenGLBuffer>
+#include <QtGui/QOpenGLFunctions>
+#include <QtGui/QOpenGLShaderProgram>
+#include <QtGui/QOpenGLVertexArrayObject>
+static QString vertexShader =
+"#version 100\n"
+"\n"
+"attribute vec3 vertexPosition;\n"
+"attribute vec3 vertexNormal;\n"
+"attribute vec3 vertexColor;\n"
+"attribute vec2 texCoord2d;\n"
+"\n"
+"uniform mat4 modelViewMatrix;\n"
+"uniform mat3 normalMatrix;\n"
+"uniform mat4 projectionMatrix;\n"
+"\n"
+"struct LightSource\n"
+"{\n"
+"    vec3 ambient;\n"
+"    vec3 diffuse;\n"
+"    vec3 specular;\n"
+"    vec3 position;\n"
+"};\n"
+"uniform LightSource lightSource;\n"
+"\n"
+"struct LightModel\n"
+"{\n"
+"    vec3 ambient;\n"
+"};\n"
+"uniform LightModel lightModel;\n"
+"\n"
+"struct Material {\n"
+"    vec3  emission;\n"
+"    vec3  specular;\n"
+"    float shininess;\n"
+"};\n"
+"uniform Material material;\n"
+"\n"
+"varying vec3 v_color;\n"
+"varying vec2 v_texCoord2d;\n"
+"\n"
+"void main()\n"
+"{\n"
+"    vec3 normal     = normalize(normalMatrix * vertexNormal);                       // normal vector              \n"
+"    vec3 position   = vec3(modelViewMatrix * vec4(vertexPosition, 1));              // vertex pos in eye coords   \n"
+"    vec3 halfVector = normalize(lightSource.position + vec3(0,0,1));                // light half vector          \n"
+"    float nDotVP    = dot(normal, normalize(lightSource.position));                 // normal . light direction   \n"
+"    float nDotHV    = max(0.f, dot(normal,  halfVector));                           // normal . light half vector \n"
+"    float pf        = mix(0.f, pow(nDotHV, material.shininess), step(0.f, nDotVP)); // power factor               \n"
+"\n"
+"    vec3 ambient    = lightSource.ambient;\n"
+"    vec3 diffuse    = lightSource.diffuse * nDotVP;\n"
+"    vec3 specular   = lightSource.specular * pf;\n"
+"    vec3 sceneColor = material.emission + vertexColor * lightModel.ambient;\n"
+"\n"
+"    v_color = clamp(sceneColor +                             \n"
+"                    ambient  * vertexColor +                 \n"
+"                    diffuse  * vertexColor +                 \n"
+"                    specular * material.specular, 0.f, 1.f );\n"
+"\n"
+"    v_texCoord2d = texCoord2d;\n"
+"\n"
+"    gl_Position = projectionMatrix * modelViewMatrix * vec4(vertexPosition, 1);\n"
+"}\n"
+;
+static QString fragmentShader =
+"#version 100\n"
+"precision lowp vec3;\n"
+"precision lowp vec2;\n"
+"uniform sampler2D texUnit;\n"
+"\n"
+"varying vec3 v_color;\n"
+"varying vec2 v_texCoord2d;\n"
+"\n"
+"void main()\n"
+"{\n"
+"    gl_FragColor = vec4(v_color, 1) * texture2D(texUnit, v_texCoord2d);\n"
+"}\n"
+;
+/*
+* Texture copied and modifided modified from:
+* <a href="https://www.opengl.org/archives/resources/code/samples/mjktips/TexShadowReflectLight.html
+*/
+static char *circles[] = {
+"................",
+"................",
+"......xxxx......",
+"....xxxxxxxx....",
+"...xxxxxxxxxx...",
+"...xxx....xxx...",
+"..xxx......xxx..",
+"..xxx......xxx..",
+"..xxx......xxx..",
+"..xxx......xxx..",
+"...xxx....xxx...",
+"...xxxxxxxxxx...",
+"....xxxxxxxx....",
+"......xxxx......",
+"................",
+"................",
+};
+struct Window : QOpenGLWindow, QOpenGLFunctions
+{
+Window() :
+m_vbo(QOpenGLBuffer::VertexBuffer),
+m_ibo(QOpenGLBuffer::IndexBuffer)
+{
+}
+void createShaderProgram()
+{
+if ( !m_pgm.addShaderFromSourceCode( QOpenGLShader::Vertex, vertexShader)) {
+qDebug() << "Error in vertex shader:" << m_pgm.log();
+exit(1);
+}
+if ( !m_pgm.addShaderFromSourceCode( QOpenGLShader::Fragment, fragmentShader)) {
+qDebug() << "Error in fragment shader:" << m_pgm.log();
+exit(1);
+}
+if ( !m_pgm.link() ) {
+qDebug() << "Error linking shader program:" << m_pgm.log();
+exit(1);
+}
+}
+void createGeometry()
+{
+// Initialize and bind the VAO that's going to capture all this vertex state
+m_vao.create();
+m_vao.bind();
+// we need 24 vertices, 24 normals, and 24 colors (6 faces, 4 vertices per face)
+// since we can't share normal data at the corners (each corner gets 3 normals)
+// and since we're not using glVertexAttribDivisor (not available in ES 2.0)
+struct Vertex {
+GLfloat position[3],
+normal  [3],
+color   [3],
+texcoord[2];
+} attribs[24]= {
+// Top face (y = 1.0f)
+{ { 1.0f, 1.0f, -1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f} },     // Green
+{ {-1.0f, 1.0f, -1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, { 0.0f, 1.0f} },     // Green
+{ {-1.0f, 1.0f,  1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, { 1.0f, 1.0f} },     // Green
+{ { 1.0f, 1.0f,  1.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f} },     // Green
+// Bottom face (y = -1.0f)
+{ { 1.0f, -1.0f,  1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.5f, 0.0f}, { 0.0f, 0.0f} },    // Orange
+{ {-1.0f, -1.0f,  1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.5f, 0.0f}, { 0.0f, 1.0f} },    // Orange
+{ {-1.0f, -1.0f, -1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.5f, 0.0f}, { 1.0f, 1.0f} },    // Orange
+{ { 1.0f, -1.0f, -1.0f}, {0.0f, -1.0f, 0.0f}, {1.0f, 0.5f, 0.0f}, { 1.0f, 0.0f} },    // Orange
+// Front face  (z = 1.0f)
+{ { 1.0f,  1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f} },     // Red
+{ {-1.0f,  1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, { 0.0f, 1.0f} },     // Red
+{ {-1.0f, -1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, { 1.0f, 1.0f} },     // Red
+{ { 1.0f, -1.0f, 1.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, { 1.0f, 0.0f} },     // Red
+// Back face (z = -1.0f)
+{ { 1.0f, -1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, { 0.0f, 0.0f} },    // Yellow
+{ {-1.0f, -1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, { 0.0f, 1.0f} },    // Yellow
+{ {-1.0f,  1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, { 1.0f, 1.0f} },    // Yellow
+{ { 1.0f,  1.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, {1.0f, 1.0f, 0.0f}, { 1.0f, 0.0f} },    // Yellow
+// Left face (x = -1.0f)
+{ {-1.0f,  1.0f,  1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 0.0f, 0.0f} },    // Blue
+{ {-1.0f,  1.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 0.0f, 1.0f} },    // Blue
+{ {-1.0f, -1.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 1.0f, 1.0f} },    // Blue
+{ {-1.0f, -1.0f,  1.0f}, {-1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f} },    // Blue
+// Right face (x = 1.0f)
+{ {1.0f,  1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, { 0.0f, 0.0f} },     // Magenta
+{ {1.0f,  1.0f,  1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, { 0.0f, 1.0f} },     // Magenta
+{ {1.0f, -1.0f,  1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, { 1.0f, 1.0f} },     // Magenta
+{ {1.0f, -1.0f, -1.0f}, {1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 1.0f}, { 1.0f, 0.0f} },     // Magenta
+};
+// Put all the attribute data in a FBO
+m_vbo.create();
+m_vbo.setUsagePattern( QOpenGLBuffer::StaticDraw );
+m_vbo.bind();
+m_vbo.allocate(&attribs, sizeof(attribs));
+// Configure the vertex streams for this attribute data layout
+m_pgm.enableAttributeArray("vertexPosition");
+m_pgm.setAttributeBuffer("vertexPosition", GL_FLOAT, offsetof(Vertex, position), 3, sizeof(Vertex) );
+m_pgm.enableAttributeArray("vertexNormal");
+m_pgm.setAttributeBuffer("vertexNormal", GL_FLOAT, offsetof(Vertex, normal), 3, sizeof(Vertex) );
+m_pgm.enableAttributeArray("vertexColor");
+m_pgm.setAttributeBuffer("vertexColor", GL_FLOAT, offsetof(Vertex, color), 3, sizeof(Vertex) );
+m_pgm.enableAttributeArray("texCoord2d");
+m_pgm.setAttributeBuffer("texCoord2d", GL_FLOAT, offsetof(Vertex, texcoord), 3, sizeof(Vertex) );
+// we need 36 indices (6 faces, 2 triangles per face, 3 vertices per triangle)
+struct {
+GLubyte cube[36];
+} indices;
+m_cnt=0; for (GLsizei i=0, v=0; v<6*4; v+=4)
+{
+// first triangle (ccw winding)
+indices.cube[i++] = v + 0;
+indices.cube[i++] = v + 1;
+indices.cube[i++] = v + 2;
+// second triangle (ccw winding)
+indices.cube[i++] = v + 0;
+indices.cube[i++] = v + 2;
+indices.cube[i++] = v + 3;
+m_cnt = i;
+}
+// Put all the index data in a IBO
+m_ibo.create();
+m_ibo.setUsagePattern( QOpenGLBuffer::StaticDraw );
+m_ibo.bind();
+m_ibo.allocate(&indices, sizeof(indices));
+// Okay, we've finished setting up the vao
+m_vao.release();
+}
+void createTexture(void)
+{
+GLubyte image[16][16][3];
+GLubyte *loc;
+int s, t;
+/* Setup RGB image for the texture. */
+loc = (GLubyte*) image;
+for (t = 0; t < 16; t++) {
+for (s = 0; s < 16; s++) {
+if (circles[t][s] == 'x') {
+/* Nice green. */
+loc[0] = 0x1f;
+loc[1] = 0x8f;
+loc[2] = 0x1f;
+} else {
+/* Light gray. */
+loc[0] = 0xaa;
+loc[1] = 0xaa;
+loc[2] = 0xaa;
+}
+loc += 3;
+}
+}
+glGenTextures  (1, &m_tex);
+glBindTexture  (GL_TEXTURE_2D, m_tex);
+glPixelStorei  (GL_UNPACK_ALIGNMENT, 1);
+glTexImage2D   (GL_TEXTURE_2D, 0, 3, 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
+glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+}
+void initializeGL()
+{
+QOpenGLFunctions::initializeOpenGLFunctions();
+createShaderProgram(); m_pgm.bind();
+// Set lighting information
+m_pgm.setUniformValue("lightSource.ambient",  QVector3D( 0.0f, 0.0f, 0.0f )); // opengl fixed-function default
+m_pgm.setUniformValue("lightSource.diffuse",  QVector3D( 1.0f, 1.0f, 1.0f )); // opengl fixed-function default
+m_pgm.setUniformValue("lightSource.specular", QVector3D( 1.0f, 1.0f, 1.0f )); // opengl fixed-function default
+m_pgm.setUniformValue("lightSource.position", QVector3D( 1.0f, 1.0f, 1.0f )); // NOT DEFAULT VALUE
+m_pgm.setUniformValue("lightModel.ambient",   QVector3D( 0.2f, 0.2f, 0.2f )); // opengl fixed-function default
+m_pgm.setUniformValue("material.emission",    QVector3D( 0.0f, 0.0f, 0.0f )); // opengl fixed-function default
+m_pgm.setUniformValue("material.specular",    QVector3D( 1.0f, 1.0f, 1.0f )); // NOT DEFAULT VALUE
+m_pgm.setUniformValue("material.shininess",   10.0f);                         // NOT DEFAULT VALUE
+createGeometry();
+m_view.setToIdentity();
+glEnable(GL_DEPTH_TEST);
+glEnable(GL_TEXTURE_2D);
+glActiveTexture(GL_TEXTURE0);
+m_pgm.setUniformValue("texUnit", 0);
+createTexture();
+glClearColor(.5f,.5f,.5f,1.f);
+}
+void resizeGL(int w, int h)
+{
+glViewport(0, 0, w, h);
+m_projection.setToIdentity();
+if (w <= h)
+m_projection.ortho(-2.f, 2.f, -2.f*h/w, 2.f*h/w, -2.f, 2.f);
+else
+m_projection.ortho(-2.f*w/h, 2.f*w/h, -2.f, 2.f, -2.f, 2.f);
+update();
+}
+void paintGL()
+{
+static unsigned cnt;
+glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+glActiveTexture(GL_TEXTURE0);
+glBindTexture(GL_TEXTURE_2D, m_tex);
+QMatrix4x4 model;
+model.rotate(cnt%360, 1,0,0);
+model.rotate(45, 0,0,1);
+QMatrix4x4 mv = m_view * model;
+m_pgm.bind();
+m_pgm.setUniformValue("modelViewMatrix", mv);
+m_pgm.setUniformValue("normalMatrix", mv.normalMatrix());
+m_pgm.setUniformValue("projectionMatrix", m_projection);
+m_vao.bind();
+glDrawElements(GL_TRIANGLES, m_cnt, GL_UNSIGNED_BYTE, 0);
+update();
+++cnt;
+}
+void keyPressEvent(QKeyEvent * ev)
+{
+switch (ev->key()) {
+case Qt::Key_Escape:
+exit(0);
+break;
+default:
+QOpenGLWindow::keyPressEvent(ev);
+break;
+}
+}
+QMatrix4x4 m_projection, m_view;
+QOpenGLShaderProgram     m_pgm;
+QOpenGLVertexArrayObject m_vao;
+QOpenGLBuffer            m_vbo;
+QOpenGLBuffer            m_ibo;
+GLuint                   m_tex;
+GLsizei                  m_cnt;
+};
+int main(int argc, char *argv[])
+{
+QGuiApplication a(argc,argv);
+Window w;
+w.setWidth(640); w.setHeight(480);
+w.show();
+return a.exec();
+}
diff --git a/hacks/gl/shader.cc b/hacks/gl/shader.cc
new file mode 100644 (file)
index 0000000..8d5f5fe
--- /dev/null
@@ -0,0 +1,50 @@
+#include <wx/wx.h>
+#include <wx/glcanvas.h>
+
+
+
+class GLCanvas
+{
+public:
+       GLCanvas (wxFrame* parent)
+       {
+               _canvas = new wxGLCanvas(parent);
+               Bind(wxEVT_PAINT, bind(&GLCanvas::paint, this);
+       }
+
+       wxWindow* get()
+       {
+               return _canvas;
+       }
+
+       void paint()
+       {
+
+       }
+
+
+private:
+       wxGLCanvas* _canvas;
+};
+
+
+
+class MyApp : public wxApp
+{
+public:
+       bool OnInit()
+       {
+               auto sizer = new wxBoxSizer(wxHORIZONTAL);
+               _frame = new wxFrame(nullptr, wxID_ANY, wxT("Hello world"));
+               _canvas = new GLCanvas(_frame);
+               sizer->Add(_canvas->get(), 1, wxEXPAND);
+               _frame->SetSizerAndFit(sizer);
+               _frame->Show();
+               return true;
+       }
+
+private:
+       wxFrame* _frame;
+       GLCanvas* _canvas;
+};
+
index 6c8d47aac098d5411b7583e925fb0bef88819520..ed5f5d4f2e0ba10324b6a1372f779fe54e829836 100644 (file)
@@ -164,6 +164,7 @@ File "%static_deps%/bin/libgpg-error-0.dll"
 File "%static_deps%/bin/libpangoft2-1.0-0.dll"
 File "%static_deps%/bin/libx264-155.dll"
 File "%static_deps%/bin/libwebp-7.dll"
+File "%static_deps%/bin/GLEW.dll"
     """, file=f)
 
     if bits == 32:
index b8861c10a651986795a57e0cdca5f6aa914dbad0..46b6fd142ff541908d3e299a47632576cbb01e66 100644 (file)
@@ -31,12 +31,12 @@ class Image;
 class BitmapText
 {
 public:
-       BitmapText (std::shared_ptr<Image> i, dcpomatic::Rect<double> r)
+       BitmapText (std::shared_ptr<const Image> i, dcpomatic::Rect<double> r)
                : image (i)
                , rectangle (r)
        {}
 
-       std::shared_ptr<Image> image;
+       std::shared_ptr<const Image> image;
        /** Area that the subtitle covers on its corresponding video, expressed in
         *  proportions of the image size; e.g. rectangle.x = 0.5 would mean that
         *  the rectangle starts half-way across the video.
index 5a8e646aa4635556fca90f8791f23e46b6468a9e..5a5cc4912f8296e004f97f84f3108f4525c4ab5d 100644 (file)
@@ -57,7 +57,7 @@ using namespace boost::placeholders;
 
 /** @param pixel_format Pixel format functor that will be used when calling ::image on PlayerVideos coming out of this
  *  butler.  This will be used (where possible) to prepare the PlayerVideos so that calling image() on them is quick.
- *  @param aligned Same as above for the `aligned' flag.
+ *  @param alignment Same as above for the `alignment' value.
  *  @param fast Same as above for the `fast' flag.
  */
 Butler::Butler (
@@ -67,8 +67,9 @@ Butler::Butler (
        int audio_channels,
        function<AVPixelFormat (AVPixelFormat)> pixel_format,
        VideoRange video_range,
-       bool aligned,
-       bool fast
+       Image::Alignment alignment,
+       bool fast,
+       bool prepare_only_proxy
        )
        : _film (film)
        , _player (player)
@@ -83,8 +84,9 @@ Butler::Butler (
        , _disable_audio (false)
        , _pixel_format (pixel_format)
        , _video_range (video_range)
-       , _aligned (aligned)
+       , _alignment (alignment)
        , _fast (fast)
+       , _prepare_only_proxy (prepare_only_proxy)
 {
        _player_video_connection = _player->Video.connect (bind (&Butler::video, this, _1, _2));
        _player_audio_connection = _player->Audio.connect (bind (&Butler::audio, this, _1, _2, _3));
@@ -323,7 +325,7 @@ try
        /* If the weak_ptr cannot be locked the video obviously no longer requires any work */
        if (video) {
                LOG_TIMING("start-prepare in %1", thread_id());
-               video->prepare (_pixel_format, _video_range, _aligned, _fast);
+               video->prepare (_pixel_format, _video_range, _alignment, _fast, _prepare_only_proxy);
                LOG_TIMING("finish-prepare in %1", thread_id());
        }
 }
index a231fd0990bf75f8f4e0f8f5bfe4eab50f9bff26..cd3891754be508c51603b8b343e3069a3a95481a 100644 (file)
@@ -45,8 +45,9 @@ public:
                int audio_channels,
                std::function<AVPixelFormat (AVPixelFormat)> pixel_format,
                VideoRange video_range,
-               bool aligned,
-               bool fast
+               Image::Alignment alignment,
+               bool fast,
+               bool prepare_only_proxy
                );
 
        ~Butler ();
@@ -124,9 +125,17 @@ private:
 
        std::function<AVPixelFormat (AVPixelFormat)> _pixel_format;
        VideoRange _video_range;
-       bool _aligned;
+       Image::Alignment _alignment;
        bool _fast;
 
+       /** true to ask PlayerVideo::prepare to only prepare the ImageProxy and not also
+        *  the final image.  We want to do this when the viewer is intending to call
+        *  PlayerVideo::raw_image() and do the things in PlayerVideo::make_imgae() itself:
+        *  this is the case for the GLVideoView which can do scale, pixfmt conversion etc.
+        *  in the shader.
+        */
+       bool _prepare_only_proxy = false;
+
        /** If we are waiting to be refilled following a seek, this is the time we were
            seeking to.
        */
index c6d7d6ec2ec06bdee841365ebbaa89eb53527b41..5edb9af2083be0993339caefa876dc09e3627e7d 100644 (file)
@@ -48,7 +48,7 @@ private:
 class ContentBitmapText : public ContentText
 {
 public:
-       ContentBitmapText (dcpomatic::ContentTime f, std::shared_ptr<Image> im, dcpomatic::Rect<double> r)
+       ContentBitmapText (dcpomatic::ContentTime f, std::shared_ptr<const Image> im, dcpomatic::Rect<double> r)
                : ContentText (f)
                , sub (im, r)
        {}
index 4a505a7e18e65996b4666d59f541e67b0f993c06..3a85a5ac6acf553eb7adc8dadc66d8530671d216 100644 (file)
@@ -59,19 +59,21 @@ DCPOMATIC_ENABLE_WARNINGS
 
 #include "i18n.h"
 
+
 using std::cout;
 using std::make_shared;
 using std::shared_ptr;
 using std::string;
-using dcp::Size;
 using dcp::ArrayData;
 using dcp::raw_convert;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
 #endif
 
+
 #define DCI_COEFFICENT (48.0 / 52.37)
 
+
 /** Construct a DCP video frame.
  *  @param frame Input frame.
  *  @param index Index of the frame within the DCP.
@@ -103,7 +105,7 @@ DCPVideo::convert_to_xyz (shared_ptr<const PlayerVideo> frame, dcp::NoteHandler
 {
        shared_ptr<dcp::OpenJPEGImage> xyz;
 
-       auto image = frame->image (bind(&PlayerVideo::keep_xyz_or_rgb, _1), VideoRange::FULL, true, false);
+       auto image = frame->image (bind(&PlayerVideo::keep_xyz_or_rgb, _1), VideoRange::FULL, false);
        if (frame->colour_conversion()) {
                xyz = dcp::rgb_to_xyz (
                        image->data()[0],
index fe27cb7dd5a80574c69cc93213fba7f0cdc619d2..1d688c31868d49450140e7381994f3431fe6bb5d 100644 (file)
@@ -41,7 +41,7 @@
 Encoder::Encoder (std::shared_ptr<const Film> film, std::weak_ptr<Job> job)
        : _film (film)
        , _job (job)
-       , _player (new Player(film))
+       , _player (new Player(film, Image::Alignment::PADDED))
 {
 
 }
index ebb607b9476b77f7969f81c3d5f78d18e1a0f68e..3f87a2ebe142893bba046fca25075b7423716faa 100644 (file)
@@ -132,6 +132,13 @@ GLError::GLError (char const* last, int e)
 }
 
 
+GLError::GLError (char const* message)
+       : runtime_error (message)
+{
+
+}
+
+
 CopyError::CopyError (string m, optional<int> n)
        : runtime_error (String::compose("%1%2", m, n ? String::compose(" (%1)", *n) : ""))
        , _message (m)
index d0ba1f068ed23e3f62e3387a3f82dd1fa03df1a7..9b7837a46bef89c695f0be7097a254566b8d8939 100644 (file)
@@ -378,6 +378,7 @@ class GLError : public std::runtime_error
 {
 public:
        GLError (char const* last, int e);
+       GLError (char const* message);
 };
 
 
index 72372fca88ab5b7a01e2411c9b30843f87e62c17..2baa99876b173adbf58e52d4774f80c5a2c393cd 100644 (file)
 
 */
 
+
 /** @file  src/ffmpeg_decoder.cc
  *  @brief A decoder using FFmpeg to decode content.
  */
 
-#include "filter.h"
-#include "exceptions.h"
-#include "image.h"
-#include "util.h"
-#include "log.h"
+
+#include "audio_buffers.h"
+#include "audio_content.h"
+#include "audio_decoder.h"
+#include "compose.hpp"
 #include "dcpomatic_log.h"
-#include "ffmpeg_decoder.h"
-#include "text_decoder.h"
+#include "exceptions.h"
 #include "ffmpeg_audio_stream.h"
-#include "ffmpeg_subtitle_stream.h"
-#include "video_filter_graph.h"
-#include "audio_buffers.h"
 #include "ffmpeg_content.h"
-#include "raw_image_proxy.h"
-#include "video_decoder.h"
+#include "ffmpeg_decoder.h"
+#include "ffmpeg_subtitle_stream.h"
 #include "film.h"
-#include "audio_decoder.h"
-#include "compose.hpp"
-#include "text_content.h"
-#include "audio_content.h"
+#include "filter.h"
 #include "frame_interval_checker.h"
+#include "image.h"
+#include "log.h"
+#include "raw_image_proxy.h"
+#include "text_content.h"
+#include "text_decoder.h"
+#include "util.h"
+#include "video_decoder.h"
+#include "video_filter_graph.h"
 #include <dcp/subtitle_string.h>
 #include <sub/ssa_reader.h>
 #include <sub/subtitle.h>
@@ -52,28 +54,22 @@ extern "C" {
 #include <libavformat/avformat.h>
 }
 #include <boost/algorithm/string.hpp>
-#include <vector>
 #include <iomanip>
 #include <iostream>
+#include <vector>
 #include <stdint.h>
 
 #include "i18n.h"
 
+
 using std::cout;
-using std::string;
-using std::vector;
-using std::list;
+using std::dynamic_pointer_cast;
+using std::make_shared;
 using std::min;
-using std::pair;
-using std::max;
-using std::map;
 using std::shared_ptr;
-using std::make_shared;
-using std::make_pair;
-using boost::is_any_of;
-using boost::split;
+using std::string;
+using std::vector;
 using boost::optional;
-using std::dynamic_pointer_cast;
 using dcp::Size;
 using namespace dcpomatic;
 
@@ -86,7 +82,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> film, shared_ptr<const FFmp
                video = make_shared<VideoDecoder>(this, c);
                _pts_offset = pts_offset (c->ffmpeg_audio_streams(), c->first_video(), c->active_video_frame_rate(film));
                /* It doesn't matter what size or pixel format this is, it just needs to be black */
-               _black_image.reset (new Image (AV_PIX_FMT_RGB24, dcp::Size (128, 128), true));
+               _black_image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size (128, 128), Image::Alignment::PADDED);
                _black_image->make_black ();
        } else {
                _pts_offset = {};
@@ -684,7 +680,7 @@ FFmpegDecoder::process_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime
        /* Note BGRA is expressed little-endian, so the first byte in the word is B, second
           G, third R, fourth A.
        */
-       auto image = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (rect->w, rect->h), true);
+       auto image = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (rect->w, rect->h), Image::Alignment::PADDED);
 
 #ifdef DCPOMATIC_HAVE_AVSUBTITLERECT_PICT
        /* Start of the first line in the subtitle */
index 61ca1ae008bc47fa99c92178ec5a863be0ef28be..dd773168aab2620a14a89e692d12610b552afb22 100644 (file)
 
 */
 
+
+#include "butler.h"
+#include "cross.h"
 #include "ffmpeg_encoder.h"
 #include "film.h"
+#include "image.h"
 #include "job.h"
+#include "log.h"
 #include "player.h"
 #include "player_video.h"
-#include "log.h"
-#include "image.h"
-#include "cross.h"
-#include "butler.h"
 #include "compose.hpp"
 #include <iostream>
 
 #include "i18n.h"
 
+
 using std::cout;
 using std::list;
 using std::make_shared;
-using std::map;
-using std::pair;
-using std::runtime_error;
 using std::shared_ptr;
 using std::string;
 using std::weak_ptr;
@@ -48,6 +47,7 @@ using namespace dcpomatic;
 using namespace boost::placeholders;
 #endif
 
+
 /** @param key Key to use to encrypt MP4 outputs */
 FFmpegEncoder::FFmpegEncoder (
        shared_ptr<const Film> film,
@@ -108,7 +108,7 @@ FFmpegEncoder::FFmpegEncoder (
        }
 
        _butler = std::make_shared<Butler>(
-               _film, _player, map, _output_audio_channels, bind(&PlayerVideo::force, _1, FFmpegFileEncoder::pixel_format(format)), VideoRange::VIDEO, true, false
+               _film, _player, map, _output_audio_channels, bind(&PlayerVideo::force, _1, FFmpegFileEncoder::pixel_format(format)), VideoRange::VIDEO, Image::Alignment::PADDED, false, false
                );
 }
 
index 533fd151c648cb083e6b6e57b3db80edf27ae19d..705557f799b3b76f434eb182fe116b4a32b03e85 100644 (file)
 */
 
 
+#include "compose.hpp"
+#include "cross.h"
 #include "ffmpeg_encoder.h"
 #include "ffmpeg_wrapper.h"
 #include "film.h"
+#include "image.h"
 #include "job.h"
+#include "log.h"
 #include "player.h"
 #include "player_video.h"
-#include "log.h"
-#include "image.h"
-#include "cross.h"
-#include "compose.hpp"
 extern "C" {
 #include <libavutil/channel_layout.h>
 }
@@ -39,13 +39,9 @@ extern "C" {
 
 using std::cout;
 using std::make_shared;
-using std::pair;
-using std::runtime_error;
 using std::shared_ptr;
 using std::string;
-using std::weak_ptr;
 using boost::bind;
-using boost::optional;
 using namespace dcpomatic;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
@@ -406,7 +402,6 @@ FFmpegFileEncoder::video (shared_ptr<PlayerVideo> video, DCPTime time)
        auto image = video->image (
                bind (&PlayerVideo::force, _1, _pixel_format),
                VideoRange::VIDEO,
-               true,
                false
                );
 
index e7d5b424dc0da3bdb052eab77f14c90f3fd0716b..4b3c3084c7f2db4a71c529f88b23f8b7c1e1e7f6 100644 (file)
@@ -122,7 +122,7 @@ FFmpegImageProxy::avio_seek (int64_t const pos, int whence)
 
 
 ImageProxy::Result
-FFmpegImageProxy::image (optional<dcp::Size>) const
+FFmpegImageProxy::image (Image::Alignment alignment, optional<dcp::Size>) const
 {
        auto constexpr name_for_errors = "FFmpegImageProxy::image";
 
@@ -205,7 +205,7 @@ FFmpegImageProxy::image (optional<dcp::Size>) const
                throw DecodeError (N_("avcodec_receive_frame"), name_for_errors, r, *_path);
        }
 
-       _image = make_shared<Image>(frame);
+       _image = make_shared<Image>(frame, alignment);
 
        av_packet_unref (&packet);
        av_frame_free (&frame);
index 21109c9d6f2dccf9baf64f11ebbc3b2ac24618bc..48317ed75c7ae16d3d3d61c30e729a9ec9c4976c 100644 (file)
@@ -32,6 +32,7 @@ public:
        FFmpegImageProxy (std::shared_ptr<Socket> socket);
 
        Result image (
+               Image::Alignment alignment,
                boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
                ) const;
 
index 64122db8d5bab6be1ab41a6d14d5c68933a66d2a..46704ebf817ae8d19118efc0baba1c90837c8421 100644 (file)
 using std::cout;
 using std::make_shared;
 using std::max;
-using std::min;
-using std::pair;
 using std::shared_ptr;
 using std::string;
-using std::vector;
 using std::weak_ptr;
 using boost::optional;
 using boost::bind;
@@ -408,7 +405,7 @@ try
                emit (bind(boost::ref(Progress), _("Examining audio, subtitles and closed captions")));
        }
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
        player->set_ignore_video ();
        if (check_loudness_done) {
                /* We don't need to analyse audio because we already loaded a suitable analysis */
index 8e6c5717b9064025a1adb03a619731a13835b347..a4e04bb626d5a146e0cd0405aa94eec850feb5c7 100644 (file)
@@ -64,8 +64,8 @@ using std::string;
 using dcp::Size;
 
 
-/** The memory alignment, in bytes, used for each row of an image if aligment is requested */
-#define ALIGNMENT 64
+/** The memory alignment, in bytes, used for each row of an image if Alignment::PADDED is requested */
+int constexpr ALIGNMENT = 64;
 
 /* U/V black value for 8-bit colour */
 static uint8_t const eight_bit_uv =    (1 << 7) - 1;
@@ -177,19 +177,19 @@ Image::crop_scale_window (
        VideoRange video_range,
        AVPixelFormat out_format,
        VideoRange out_video_range,
-       bool out_aligned,
+       Alignment out_alignment,
        bool fast
        ) const
 {
        /* Empirical testing suggests that sws_scale() will crash if
-          the input image is not aligned.
+          the input image is not padded.
        */
-       DCPOMATIC_ASSERT (aligned ());
+       DCPOMATIC_ASSERT (alignment() == Alignment::PADDED);
 
        DCPOMATIC_ASSERT (out_size.width >= inter_size.width);
        DCPOMATIC_ASSERT (out_size.height >= inter_size.height);
 
-       auto out = make_shared<Image>(out_format, out_size, out_aligned);
+       auto out = make_shared<Image>(out_format, out_size, out_alignment);
        out->make_black ();
 
        auto in_desc = av_pix_fmt_desc_get (_pixel_format);
@@ -310,27 +310,27 @@ Image::crop_scale_window (
 }
 
 shared_ptr<Image>
-Image::convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned, bool fast) const
+Image::convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, Alignment out_alignment, bool fast) const
 {
-       return scale(size(), yuv_to_rgb, out_format, out_aligned, fast);
+       return scale(size(), yuv_to_rgb, out_format, out_alignment, fast);
 }
 
 /** @param out_size Size to scale to.
  *  @param yuv_to_rgb YUVToRGB transform transform to use, if required.
  *  @param out_format Output pixel format.
- *  @param out_aligned true to make an aligned output image.
+ *  @param out_aligment Output alignment.
  *  @param fast Try to be fast at the possible expense of quality; at present this means using
  *  fast bilinear rather than bicubic scaling.
  */
 shared_ptr<Image>
-Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned, bool fast) const
+Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, Alignment out_alignment, bool fast) const
 {
        /* Empirical testing suggests that sws_scale() will crash if
-          the input image is not aligned.
+          the input image alignment is not PADDED.
        */
-       DCPOMATIC_ASSERT (aligned ());
+       DCPOMATIC_ASSERT (alignment() == Alignment::PADDED);
 
-       auto scaled = make_shared<Image>(out_format, out_size, out_aligned);
+       auto scaled = make_shared<Image>(out_format, out_size, out_alignment);
        auto scale_context = sws_getContext (
                size().width, size().height, pixel_format(),
                out_size.width, out_size.height, out_format,
@@ -736,7 +736,7 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
        }
        case AV_PIX_FMT_YUV420P:
        {
-               auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, false, false);
+               auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false);
                dcp::Size const ts = size();
                dcp::Size const os = yuv->size();
                for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) {
@@ -771,7 +771,7 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
        }
        case AV_PIX_FMT_YUV420P10:
        {
-               auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, false, false);
+               auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false);
                dcp::Size const ts = size();
                dcp::Size const os = yuv->size();
                for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) {
@@ -806,7 +806,7 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
        }
        case AV_PIX_FMT_YUV422P10LE:
        {
-               auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, false, false);
+               auto yuv = other->convert_pixel_format (dcp::YUVToRGB::REC709, _pixel_format, Alignment::COMPACT, false);
                dcp::Size const ts = size();
                dcp::Size const os = yuv->size();
                for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) {
@@ -934,16 +934,17 @@ Image::bytes_per_pixel (int c) const
  *
  *  @param p Pixel format.
  *  @param s Size in pixels.
- *  @param aligned true to make each row of this image aligned to a ALIGNMENT-byte boundary.
+ *  @param alignment PADDED to make each row of this image aligned to a ALIGNMENT-byte boundary, otherwise COMPACT.
  */
-Image::Image (AVPixelFormat p, dcp::Size s, bool aligned)
+Image::Image (AVPixelFormat p, dcp::Size s, Alignment alignment)
        : _size (s)
        , _pixel_format (p)
-       , _aligned (aligned)
+       , _alignment (alignment)
 {
        allocate ();
 }
 
+
 void
 Image::allocate ()
 {
@@ -958,7 +959,7 @@ Image::allocate ()
 
        for (int i = 0; i < planes(); ++i) {
                _line_size[i] = ceil (_size.width * bytes_per_pixel(i));
-               _stride[i] = stride_round_up (i, _line_size, _aligned ? ALIGNMENT : 1);
+               _stride[i] = stride_round_up (i, _line_size, _alignment == Alignment::PADDED ? ALIGNMENT : 1);
 
                /* The assembler function ff_rgb24ToY_avx (in libswscale/x86/input.asm)
                   uses a 16-byte fetch to read three bytes (R/G/B) of image data.
@@ -1011,7 +1012,7 @@ Image::Image (Image const & other)
        : std::enable_shared_from_this<Image>(other)
        , _size (other._size)
        , _pixel_format (other._pixel_format)
-       , _aligned (other._aligned)
+       , _alignment (other._alignment)
 {
        allocate ();
 
@@ -1027,10 +1028,10 @@ Image::Image (Image const & other)
        }
 }
 
-Image::Image (AVFrame const * frame)
+Image::Image (AVFrame const * frame, Alignment alignment)
        : _size (frame->width, frame->height)
        , _pixel_format (static_cast<AVPixelFormat>(frame->format))
-       , _aligned (true)
+       , _alignment (alignment)
 {
        DCPOMATIC_ASSERT (_pixel_format != AV_PIX_FMT_NONE);
 
@@ -1049,10 +1050,10 @@ Image::Image (AVFrame const * frame)
        }
 }
 
-Image::Image (shared_ptr<const Image> other, bool aligned)
+Image::Image (shared_ptr<const Image> other, Alignment alignment)
        : _size (other->_size)
        , _pixel_format (other->_pixel_format)
-       , _aligned (aligned)
+       , _alignment (alignment)
 {
        allocate ();
 
@@ -1093,7 +1094,7 @@ Image::swap (Image & other)
                std::swap (_stride[i], other._stride[i]);
        }
 
-       std::swap (_aligned, other._aligned);
+       std::swap (_alignment, other._alignment);
 }
 
 Image::~Image ()
@@ -1131,22 +1132,23 @@ Image::size () const
        return _size;
 }
 
-bool
-Image::aligned () const
+Image::Alignment
+Image::alignment () const
 {
-       return _aligned;
+       return _alignment;
 }
 
 
 PositionImage
-merge (list<PositionImage> images)
+merge (list<PositionImage> images, Image::Alignment alignment)
 {
        if (images.empty ()) {
                return {};
        }
 
        if (images.size() == 1) {
-               return images.front ();
+               images.front().image = Image::ensure_alignment(images.front().image, alignment);
+               return images.front();
        }
 
        dcpomatic::Rect<int> all (images.front().position, images.front().image->size().width, images.front().image->size().height);
@@ -1154,7 +1156,7 @@ merge (list<PositionImage> images)
                all.extend (dcpomatic::Rect<int>(i.position, i.image->size().width, i.image->size().height));
        }
 
-       auto merged = make_shared<Image>(images.front().image->pixel_format(), dcp::Size(all.width, all.height), true);
+       auto merged = make_shared<Image>(images.front().image->pixel_format(), dcp::Size(all.width, all.height), alignment);
        merged->make_transparent ();
        for (auto const& i: images) {
                merged->alpha_blend (i.image, i.position - all.position());
@@ -1167,7 +1169,7 @@ merge (list<PositionImage> images)
 bool
 operator== (Image const & a, Image const & b)
 {
-       if (a.planes() != b.planes() || a.pixel_format() != b.pixel_format() || a.aligned() != b.aligned()) {
+       if (a.planes() != b.planes() || a.pixel_format() != b.pixel_format() || a.alignment() != b.alignment()) {
                return false;
        }
 
@@ -1312,16 +1314,18 @@ Image::fade (float f)
        }
 }
 
+
 shared_ptr<const Image>
-Image::ensure_aligned (shared_ptr<const Image> image)
+Image::ensure_alignment (shared_ptr<const Image> image, Image::Alignment alignment)
 {
-       if (image->aligned()) {
+       if (image->alignment() == alignment) {
                return image;
        }
 
-       return make_shared<Image>(image, true);
+       return make_shared<Image>(image, alignment);
 }
 
+
 size_t
 Image::memory_used () const
 {
@@ -1393,7 +1397,7 @@ Image::as_png () const
        DCPOMATIC_ASSERT (bytes_per_pixel(0) == 4);
        DCPOMATIC_ASSERT (planes() == 1);
        if (pixel_format() != AV_PIX_FMT_RGBA) {
-               return convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, true, false)->as_png();
+               return convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, Image::Alignment::PADDED, false)->as_png();
        }
 
        /* error handling? */
index cb8f11ffc27d0eab3ab4ba86d4646b4498bc7b7a..128b546b5cc7ac2a511e592fe98ae388d36556dd 100644 (file)
@@ -40,10 +40,15 @@ class Socket;
 class Image : public std::enable_shared_from_this<Image>
 {
 public:
-       Image (AVPixelFormat p, dcp::Size s, bool aligned);
-       explicit Image (AVFrame const *);
+       enum class Alignment {
+               COMPACT,
+               PADDED
+       };
+
+       Image (AVPixelFormat p, dcp::Size s, Alignment alignment);
+       explicit Image (AVFrame const *, Alignment alignment);
        explicit Image (Image const &);
-       Image (std::shared_ptr<const Image>, bool);
+       Image (std::shared_ptr<const Image>, Alignment alignment);
        Image& operator= (Image const &);
        ~Image ();
 
@@ -53,7 +58,7 @@ public:
        /** @return array of sizes of the data in each line, in bytes (including any alignment padding) */
        int const * stride () const;
        dcp::Size size () const;
-       bool aligned () const;
+       Alignment alignment () const;
 
        int planes () const;
        int vertical_factor (int) const;
@@ -61,8 +66,8 @@ public:
        dcp::Size sample_size (int) const;
        float bytes_per_pixel (int) const;
 
-       std::shared_ptr<Image> convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool aligned, bool fast) const;
-       std::shared_ptr<Image> scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool aligned, bool fast) const;
+       std::shared_ptr<Image> convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, Alignment alignment, bool fast) const;
+       std::shared_ptr<Image> scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, Alignment alignment, bool fast) const;
        std::shared_ptr<Image> crop_scale_window (
                Crop crop,
                dcp::Size inter_size,
@@ -71,7 +76,7 @@ public:
                VideoRange video_range,
                AVPixelFormat out_format,
                VideoRange out_video_range,
-               bool aligned,
+               Alignment alignment,
                bool fast
                ) const;
 
@@ -94,7 +99,7 @@ public:
 
        void png_error (char const * message);
 
-       static std::shared_ptr<const Image> ensure_aligned (std::shared_ptr<const Image> image);
+       static std::shared_ptr<const Image> ensure_alignment (std::shared_ptr<const Image> image, Alignment alignment);
 
 private:
        friend struct pixel_formats_test;
@@ -112,10 +117,10 @@ private:
        uint8_t** _data; ///< array of pointers to components
        int* _line_size; ///< array of sizes of the data in each line, in bytes (without any alignment padding bytes)
        int* _stride; ///< array of strides for each line, in bytes (including any alignment padding bytes)
-       bool _aligned;
+       Alignment _alignment;
 };
 
-extern PositionImage merge (std::list<PositionImage> images);
+extern PositionImage merge (std::list<PositionImage> images, Image::Alignment alignment);
 extern bool operator== (Image const & a, Image const & b);
 
 #endif
index 89d1517ce5abf380d9e30c3a09777bd17a5a0293..ae12d7adb3570363304326250fe55474b3827039 100644 (file)
@@ -67,7 +67,7 @@ ImageExaminer::ImageExaminer (shared_ptr<const Film> film, shared_ptr<const Imag
                delete[] buffer;
        } else {
                FFmpegImageProxy proxy(content->path(0));
-               _video_size = proxy.image().image->size();
+               _video_size = proxy.image(Image::Alignment::COMPACT).image->size();
        }
 
        if (content->still ()) {
index 22946ed98a56b1272421e053ceae35df58cdf811..a37be580fcf062fbb247e1d02285e532d49279da 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
 #ifndef DCPOMATIC_IMAGE_PROXY_H
 #define DCPOMATIC_IMAGE_PROXY_H
 
+
 /** @file  src/lib/image_proxy.h
  *  @brief ImageProxy and subclasses.
  */
 
+
+#include "image.h"
 extern "C" {
 #include <libavutil/pixfmt.h>
 }
@@ -32,6 +36,7 @@ extern "C" {
 #include <boost/optional.hpp>
 #include <boost/utility.hpp>
 
+
 class Image;
 class Socket;
 
@@ -43,6 +48,7 @@ namespace cxml {
        class Node;
 }
 
+
 /** @class ImageProxy
  *  @brief A class which holds an Image, and can produce it on request.
  *
@@ -64,7 +70,7 @@ public:
        ImageProxy& operator= (ImageProxy const&) = delete;
 
        struct Result {
-               Result (std::shared_ptr<Image> image_, int log2_scaling_)
+               Result (std::shared_ptr<const Image> image_, int log2_scaling_)
                        : image (image_)
                        , log2_scaling (log2_scaling_)
                        , error (false)
@@ -76,8 +82,7 @@ public:
                        , error (error_)
                {}
 
-               /** Image (which will be aligned) */
-               std::shared_ptr<Image> image;
+               std::shared_ptr<const Image> image;
                /** log2 of any scaling down that has already been applied to the image;
                 *  e.g. if the image is already half the size of the original, this value
                 *  will be 1.
@@ -92,6 +97,7 @@ public:
         *  can be used as an optimisation.
         */
        virtual Result image (
+               Image::Alignment alignment,
                boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
                ) const = 0;
 
@@ -103,10 +109,12 @@ public:
         *  This method may be called in a different thread to image().
         *  @return log2 of any scaling down that will be applied to the image.
         */
-       virtual int prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const { return 0; }
+       virtual int prepare (Image::Alignment, boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const { return 0; }
        virtual size_t memory_used () const = 0;
 };
 
+
 std::shared_ptr<ImageProxy> image_proxy_factory (std::shared_ptr<cxml::Node> xml, std::shared_ptr<Socket> socket);
 
+
 #endif
index 144da396d24ee619970c679de49731378a69963c..00d3cf2ef20b4edfb473dbcaa442e73bdff9a9b5 100644 (file)
@@ -42,10 +42,8 @@ DCPOMATIC_ENABLE_WARNINGS
 
 using std::cout;
 using std::dynamic_pointer_cast;
-using std::make_pair;
 using std::make_shared;
 using std::max;
-using std::pair;
 using std::shared_ptr;
 using std::string;
 using boost::optional;
@@ -108,7 +106,7 @@ J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc
        if (xml->optional_number_child<int>("Eye")) {
                _eye = static_cast<dcp::Eye>(xml->number_child<int>("Eye"));
        }
-       shared_ptr<ArrayData> data(new ArrayData(xml->number_child<int>("Size")));
+       auto data = make_shared<ArrayData>(xml->number_child<int>("Size"));
        /* This only matters when we are using J2KImageProxy for the preview, which
           will never use this constructor (which is only used for passing data to
           encode servers).  So we can put anything in here.  It's a bit of a hack.
@@ -120,7 +118,7 @@ J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc
 
 
 int
-J2KImageProxy::prepare (optional<dcp::Size> target_size) const
+J2KImageProxy::prepare (Image::Alignment alignment, optional<dcp::Size> target_size) const
 {
        boost::mutex::scoped_lock lm (_mutex);
 
@@ -145,7 +143,7 @@ J2KImageProxy::prepare (optional<dcp::Size> target_size) const
        try {
                /* XXX: should check that potentially trashing _data here doesn't matter */
                auto decompressed = dcp::decompress_j2k (const_cast<uint8_t*>(_data->data()), _data->size(), reduce);
-               _image.reset (new Image (_pixel_format, decompressed->size(), true));
+               _image = make_shared<Image>(_pixel_format, decompressed->size(), alignment);
 
                int const shift = 16 - decompressed->precision (0);
 
@@ -169,7 +167,7 @@ J2KImageProxy::prepare (optional<dcp::Size> target_size) const
                        }
                }
        } catch (dcp::J2KDecompressionError& e) {
-               _image = make_shared<Image>(_pixel_format, _size, true);
+               _image = make_shared<Image>(_pixel_format, _size, alignment);
                _image->make_black ();
                _error = true;
        }
@@ -182,9 +180,9 @@ J2KImageProxy::prepare (optional<dcp::Size> target_size) const
 
 
 ImageProxy::Result
-J2KImageProxy::image (optional<dcp::Size> target_size) const
+J2KImageProxy::image (Image::Alignment alignment, optional<dcp::Size> target_size) const
 {
-       int const r = prepare (target_size);
+       int const r = prepare (alignment, target_size);
 
        /* I think this is safe without a lock on mutex.  _image is guaranteed to be
           set up when prepare() has happened.
index 5235d8e42522d02c42dd7566ec18bc6c8301853c..d925bef864fe9129fc59dd8246b4b305325ea55e 100644 (file)
@@ -57,6 +57,7 @@ public:
        J2KImageProxy (dcp::ArrayData data, dcp::Size size, AVPixelFormat pixel_format);
 
        Result image (
+               Image::Alignment alignment,
                boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
                ) const;
 
@@ -64,7 +65,7 @@ public:
        void write_to_socket (std::shared_ptr<Socket>) const;
        /** @return true if our image is definitely the same as another, false if it is probably not */
        bool same (std::shared_ptr<const ImageProxy>) const;
-       int prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const;
+       int prepare (Image::Alignment alignment, boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const;
 
        std::shared_ptr<const dcp::Data> j2k () const {
                return _data;
index 5de089ba91aa9ad297bbe299a2b2ad9b076ec76e..7951926e63f06ae47adb2071433058beccc96a7d 100644 (file)
@@ -70,7 +70,6 @@ using std::dynamic_pointer_cast;
 using std::list;
 using std::make_pair;
 using std::make_shared;
-using std::map;
 using std::max;
 using std::min;
 using std::min;
@@ -95,11 +94,12 @@ int const PlayerProperty::DCP_DECODE_REDUCTION = 704;
 int const PlayerProperty::PLAYBACK_LENGTH = 705;
 
 
-Player::Player (shared_ptr<const Film> film)
+Player::Player (shared_ptr<const Film> film, Image::Alignment subtitle_alignment)
        : _film (film)
        , _suspended (0)
        , _tolerant (film->tolerant())
        , _audio_merger (_film->audio_frame_rate())
+       , _subtitle_alignment (subtitle_alignment)
 {
        construct ();
 }
@@ -331,7 +331,7 @@ Player::set_video_container_size (dcp::Size s)
 
                _video_container_size = s;
 
-               _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
+               _black_image = make_shared<Image>(AV_PIX_FMT_RGB24, _video_container_size, Image::Alignment::PADDED);
                _black_image->make_black ();
        }
 
@@ -827,7 +827,7 @@ Player::open_subtitles_for_frame (DCPTime time) const
                return {};
        }
 
-       return merge (captions);
+       return merge (captions, _subtitle_alignment);
 }
 
 
@@ -1055,7 +1055,7 @@ Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const TextContent> wc, C
        }
 
        dcp::Size scaled_size (width, height);
-       ps.bitmap.push_back (BitmapText(image->scale(scaled_size, dcp::YUVToRGB::REC601, image->pixel_format(), true, _fast), subtitle.sub.rectangle));
+       ps.bitmap.push_back (BitmapText(image->scale(scaled_size, dcp::YUVToRGB::REC601, image->pixel_format(), Image::Alignment::PADDED, _fast), subtitle.sub.rectangle));
        DCPTime from (content_time_to_dcp (piece, subtitle.from()));
 
        _active_texts[static_cast<int>(text->type())].add_from (wc, ps, from);
index 6cefbe2323bc6363ba59ca424d1cd1846ecee7cc..b74aeeefd8f0ab994eb604fcd77ca421f11ec64c 100644 (file)
@@ -34,6 +34,7 @@
 #include "content_video.h"
 #include "empty.h"
 #include "film.h"
+#include "image.h"
 #include "piece.h"
 #include "player_text.h"
 #include "position_image.h"
@@ -76,7 +77,7 @@ public:
 class Player : public std::enable_shared_from_this<Player>
 {
 public:
-       Player (std::shared_ptr<const Film>);
+       Player (std::shared_ptr<const Film>, Image::Alignment subtitle_alignment);
        Player (std::shared_ptr<const Film>, std::shared_ptr<const Playlist> playlist);
 
        Player (Player const& Player) = delete;
@@ -233,6 +234,9 @@ private:
 
        dcpomatic::DCPTime _playback_length;
 
+       /** Alignment for subtitle images that we create */
+       Image::Alignment _subtitle_alignment = Image::Alignment::PADDED;
+
        boost::signals2::scoped_connection _film_changed_connection;
        boost::signals2::scoped_connection _playlist_change_connection;
        boost::signals2::scoped_connection _playlist_content_change_connection;
index b0e75972ce342338c2ce152ddb6f0e0a313db17a..7c36af31b54bbbb7e8c764b9f0b3dcba89145e5e 100644 (file)
@@ -94,7 +94,7 @@ PlayerVideo::PlayerVideo (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket
        if (node->optional_number_child<int>("SubtitleX")) {
 
                auto image = make_shared<Image> (
-                       AV_PIX_FMT_BGRA, dcp::Size(node->number_child<int>("SubtitleWidth"), node->number_child<int>("SubtitleHeight")), true
+                       AV_PIX_FMT_BGRA, dcp::Size(node->number_child<int>("SubtitleWidth"), node->number_child<int>("SubtitleHeight")), Image::Alignment::PADDED
                        );
 
                image->read_from_socket (socket);
@@ -110,33 +110,40 @@ PlayerVideo::set_text (PositionImage image)
 }
 
 shared_ptr<Image>
-PlayerVideo::image (function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool aligned, bool fast) const
+PlayerVideo::image (function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool fast) const
 {
        /* XXX: this assumes that image() and prepare() are only ever called with the same parameters (except crop, inter size, out size, fade) */
 
        boost::mutex::scoped_lock lm (_mutex);
        if (!_image || _crop != _image_crop || _inter_size != _image_inter_size || _out_size != _image_out_size || _fade != _image_fade) {
-               make_image (pixel_format, video_range, aligned, fast);
+               make_image (pixel_format, video_range, fast);
        }
        return _image;
 }
 
+
+shared_ptr<const Image>
+PlayerVideo::raw_image () const
+{
+       return _in->image(Image::Alignment::COMPACT, _inter_size).image;
+}
+
+
 /** Create an image for this frame.  A lock must be held on _mutex.
  *  @param pixel_format Function which is called to decide what pixel format the output image should be;
  *  it is passed the pixel format of the input image from the ImageProxy, and should return the desired
  *  output pixel format.  Two functions force and keep_xyz_or_rgb are provided for use here.
- *  @param aligned true if the output image should be aligned to 32-byte boundaries.
  *  @param fast true to be fast at the expense of quality.
  */
 void
-PlayerVideo::make_image (function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool aligned, bool fast) const
+PlayerVideo::make_image (function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool fast) const
 {
        _image_crop = _crop;
        _image_inter_size = _inter_size;
        _image_out_size = _out_size;
        _image_fade = _fade;
 
-       auto prox = _in->image (_inter_size);
+       auto prox = _in->image (Image::Alignment::PADDED, _inter_size);
        _error = prox.error;
 
        auto total_crop = _crop;
@@ -172,11 +179,11 @@ PlayerVideo::make_image (function<AVPixelFormat (AVPixelFormat)> pixel_format, V
        }
 
        _image = prox.image->crop_scale_window (
-               total_crop, _inter_size, _out_size, yuv_to_rgb, _video_range, pixel_format (prox.image->pixel_format()), video_range, aligned, fast
+               total_crop, _inter_size, _out_size, yuv_to_rgb, _video_range, pixel_format (prox.image->pixel_format()), video_range, Image::Alignment::COMPACT, fast
                );
 
        if (_text) {
-               _image->alpha_blend (Image::ensure_aligned (_text->image), _text->position);
+               _image->alpha_blend (_text->image, _text->position);
        }
 
        if (_fade) {
@@ -290,12 +297,12 @@ PlayerVideo::keep_xyz_or_rgb (AVPixelFormat p)
 }
 
 void
-PlayerVideo::prepare (function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool aligned, bool fast)
+PlayerVideo::prepare (function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, Image::Alignment alignment, bool fast, bool proxy_only)
 {
-       _in->prepare (_inter_size);
+       _in->prepare (alignment, _inter_size);
        boost::mutex::scoped_lock lm (_mutex);
-       if (!_image) {
-               make_image (pixel_format, video_range, aligned, fast);
+       if (!_image && !proxy_only) {
+               make_image (pixel_format, video_range, fast);
        }
 }
 
index f296848320a77a053d863d5ded423c05015d7dbc..237d2e3fe2858083d6a6d165dd0ac63999fee693 100644 (file)
 #define DCPOMATIC_PLAYER_VIDEO_H
 
 
-#include "types.h"
-#include "position.h"
-#include "dcpomatic_time.h"
 #include "colour_conversion.h"
+#include "dcpomatic_time.h"
+#include "image.h"
+#include "position.h"
 #include "position_image.h"
+#include "types.h"
 extern "C" {
 #include <libavutil/pixfmt.h>
 }
@@ -70,9 +71,13 @@ public:
        std::shared_ptr<PlayerVideo> shallow_copy () const;
 
        void set_text (PositionImage);
+       boost::optional<PositionImage> text () const {
+               return _text;
+       }
 
-       void prepare (std::function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool aligned, bool fast);
-       std::shared_ptr<Image> image (std::function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool aligned, bool fast) const;
+       void prepare (std::function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, Image::Alignment alignment, bool fast, bool proxy_only);
+       std::shared_ptr<Image> image (std::function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool fast) const;
+       std::shared_ptr<const Image> raw_image () const;
 
        static AVPixelFormat force (AVPixelFormat, AVPixelFormat);
        static AVPixelFormat keep_xyz_or_rgb (AVPixelFormat);
@@ -105,6 +110,10 @@ public:
                return _inter_size;
        }
 
+       dcp::Size out_size () const {
+               return _out_size;
+       }
+
        bool same (std::shared_ptr<const PlayerVideo> other) const;
 
        size_t memory_used () const;
@@ -118,7 +127,7 @@ public:
        }
 
 private:
-       void make_image (std::function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool aligned, bool fast) const;
+       void make_image (std::function<AVPixelFormat (AVPixelFormat)> pixel_format, VideoRange video_range, bool fast) const;
 
        std::shared_ptr<const ImageProxy> _in;
        Crop _crop;
index 2b7e7080a68df0bcb8e7228ae21cd2f6827c344b..ee352647ca83826e0e359d857ad2928a9d96ea4b 100644 (file)
@@ -35,12 +35,12 @@ class PositionImage
 public:
        PositionImage () {}
 
-       PositionImage (std::shared_ptr<Image> i, Position<int> p)
+       PositionImage (std::shared_ptr<const Image> i, Position<int> p)
                : image (i)
                , position (p)
        {}
 
-       std::shared_ptr<Image> image;
+       std::shared_ptr<const Image> image;
        Position<int> position;
 
        bool same (PositionImage const & other) const;
index fed40c45e7ec783faaa31fa94b55f91810177dc2..fb0d16df8a5b6cc29cef87a5054d85ccdc8dec5e 100644 (file)
@@ -58,15 +58,16 @@ RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc
                xml->number_child<int>("Width"), xml->number_child<int>("Height")
                );
 
-       _image = make_shared<Image>(static_cast<AVPixelFormat>(xml->number_child<int>("PixelFormat")), size, true);
+       _image = make_shared<Image>(static_cast<AVPixelFormat>(xml->number_child<int>("PixelFormat")), size, Image::Alignment::PADDED);
        _image->read_from_socket (socket);
 }
 
 
 ImageProxy::Result
-RawImageProxy::image (optional<dcp::Size>) const
+RawImageProxy::image (Image::Alignment alignment, optional<dcp::Size>) const
 {
-       return Result (_image, 0);
+       /* This ensure_alignment could be wasteful */
+       return Result (Image::ensure_alignment(_image, alignment), 0);
 }
 
 
@@ -95,7 +96,7 @@ RawImageProxy::same (shared_ptr<const ImageProxy> other) const
                return false;
        }
 
-       return (*_image.get()) == (*rp->image().image.get());
+       return (*_image.get()) == (*rp->image(_image->alignment()).image.get());
 }
 
 
index ec30f5a29aa3f8ad945c83f106b81f623c516956..c9885654bd3ae3e830c7a6f635d25d205999f8d8 100644 (file)
@@ -33,6 +33,7 @@ public:
        RawImageProxy (std::shared_ptr<cxml::Node> xml, std::shared_ptr<Socket> socket);
 
        Result image (
+               Image::Alignment alignment,
                boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
                ) const;
 
index 7bb7d6b456990d2b12f21f1a4ec343d8534290d2..94b41285627270868d97a95792659445cae102ce 100644 (file)
@@ -48,8 +48,6 @@ using std::min;
 using std::pair;
 using std::shared_ptr;
 using std::string;
-using boost::optional;
-using boost::algorithm::replace_all;
 using namespace dcpomatic;
 
 
@@ -95,8 +93,10 @@ set_source_rgba (Cairo::RefPtr<Cairo::Context> context, dcp::Colour colour, floa
 static shared_ptr<Image>
 create_image (dcp::Size size)
 {
-       /* FFmpeg BGRA means first byte blue, second byte green, third byte red, fourth byte alpha */
-       auto image = make_shared<Image>(AV_PIX_FMT_BGRA, size, false);
+       /* FFmpeg BGRA means first byte blue, second byte green, third byte red, fourth byte alpha.
+        * This must be COMPACT as we're using it with Cairo::ImageSurface::create
+        */
+       auto image = make_shared<Image>(AV_PIX_FMT_BGRA, size, Image::Alignment::COMPACT);
        image->make_black ();
        return image;
 }
@@ -105,6 +105,11 @@ create_image (dcp::Size size)
 static Cairo::RefPtr<Cairo::ImageSurface>
 create_surface (shared_ptr<Image> image)
 {
+       /* XXX: I don't think it's guaranteed that format_stride_for_width will return a stride without any padding,
+        * so it's lucky that this works.
+        */
+       DCPOMATIC_ASSERT (image->alignment() == Image::Alignment::COMPACT);
+       DCPOMATIC_ASSERT (image->pixel_format() == AV_PIX_FMT_BGRA);
        return Cairo::ImageSurface::create (
                image->data()[0],
                Cairo::FORMAT_ARGB32,
index 6ee6ed079879e014c33df5c2c4bc36e79b336881..8111154e348f00b619e0f81229f3d9e7b04993a2 100644 (file)
@@ -61,7 +61,7 @@ TextDecoder::TextDecoder (
  *  of the video frame)
  */
 void
-TextDecoder::emit_bitmap_start (ContentTime from, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
+TextDecoder::emit_bitmap_start (ContentTime from, shared_ptr<const Image> image, dcpomatic::Rect<double> rect)
 {
        BitmapStart (ContentBitmapText (from, image, rect));
        _position = from;
@@ -286,7 +286,7 @@ TextDecoder::emit_plain (ContentTimePeriod period, sub::Subtitle const & s)
  *  of the video frame)
  */
 void
-TextDecoder::emit_bitmap (ContentTimePeriod period, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
+TextDecoder::emit_bitmap (ContentTimePeriod period, shared_ptr<const Image> image, dcpomatic::Rect<double> rect)
 {
        emit_bitmap_start (period.from, image, rect);
        emit_stop (period.to);
index 6e96b6b914abd109f52a2c3f4af4c161a9cc385f..d1355afb02c4b7eaf135917e5dccc487641d7947 100644 (file)
@@ -52,8 +52,8 @@ public:
                return _position;
        }
 
-       void emit_bitmap_start (dcpomatic::ContentTime from, std::shared_ptr<Image> image, dcpomatic::Rect<double> rect);
-       void emit_bitmap (dcpomatic::ContentTimePeriod period, std::shared_ptr<Image> image, dcpomatic::Rect<double> rect);
+       void emit_bitmap_start (dcpomatic::ContentTime from, std::shared_ptr<const Image> image, dcpomatic::Rect<double> rect);
+       void emit_bitmap (dcpomatic::ContentTimePeriod period, std::shared_ptr<const Image> image, dcpomatic::Rect<double> rect);
        void emit_plain_start (dcpomatic::ContentTime from, std::list<dcp::SubtitleString> s);
        void emit_plain_start (dcpomatic::ContentTime from, sub::Subtitle const & subtitle);
        void emit_plain (dcpomatic::ContentTimePeriod period, std::list<dcp::SubtitleString> s);
index 0646a478772a086704cfcb6b7e4d2d87408ab18c..d3af74376a3cd2349eeceafa9ab3ab35f5dc9423 100644 (file)
  *  @brief Some utility functions and classes.
  */
 
+
 #define UNICODE 1
 
-#include "util.h"
-#include "exceptions.h"
-#include "dcp_content_type.h"
-#include "filter.h"
+
+#include "audio_buffers.h"
+#include "audio_processor.h"
 #include "cinema_sound_processor.h"
+#include "compose.hpp"
 #include "config.h"
-#include "ratio.h"
-#include "job.h"
 #include "cross.h"
-#include "video_content.h"
-#include "rect.h"
-#include "digester.h"
-#include "audio_processor.h"
 #include "crypto.h"
-#include "compose.hpp"
-#include "audio_buffers.h"
-#include "string_text.h"
-#include "font.h"
-#include "render_text.h"
+#include "dcp_content_type.h"
+#include "digester.h"
+#include "exceptions.h"
 #include "ffmpeg_image_proxy.h"
+#include "filter.h"
+#include "font.h"
 #include "image.h"
-#include "text_decoder.h"
+#include "job.h"
 #include "job_manager.h"
+#include "ratio.h"
+#include "rect.h"
+#include "render_text.h"
+#include "string_text.h"
+#include "text_decoder.h"
+#include "util.h"
+#include "video_content.h"
 #include "warnings.h"
 #include <dcp/decrypted_kdm.h>
 #include <dcp/locale_convert.h>
@@ -93,25 +95,23 @@ DCPOMATIC_ENABLE_WARNINGS
 
 #include "i18n.h"
 
-using std::string;
-using std::wstring;
-using std::setfill;
-using std::ostream;
+
+using std::bad_alloc;
+using std::cout;
 using std::endl;
-using std::vector;
-using std::min;
-using std::max;
-using std::map;
-using std::list;
-using std::multimap;
 using std::istream;
+using std::list;
+using std::make_pair;
+using std::make_shared;
+using std::map;
+using std::min;
+using std::ostream;
 using std::pair;
-using std::cout;
-using std::bad_alloc;
 using std::set_terminate;
-using std::make_pair;
 using std::shared_ptr;
-using std::make_shared;
+using std::string;
+using std::vector;
+using std::wstring;
 using boost::thread;
 using boost::optional;
 using boost::lexical_cast;
@@ -122,6 +122,7 @@ using dcp::raw_convert;
 using dcp::locale_convert;
 using namespace dcpomatic;
 
+
 /** Path to our executable, required by the stacktrace stuff and filled
  *  in during App::onInit().
  */
@@ -956,7 +957,7 @@ emit_subtitle_image (ContentTimePeriod period, dcp::SubtitleImage sub, dcp::Size
 {
        /* XXX: this is rather inefficient; decoding the image just to get its size */
        FFmpegImageProxy proxy (sub.png_image());
-       auto image = proxy.image().image;
+       auto image = proxy.image(Image::Alignment::PADDED).image;
        /* set up rect with height and width */
        dcpomatic::Rect<double> rect(0, 0, image->size().width / double(size.width), image->size().height / double(size.height));
 
index a61da6773bbb8517330895aa2a8b01eae3e39c76..0c7e23b050b2e2b0fe6b98940616df0c83a62529 100644 (file)
@@ -38,7 +38,6 @@ using std::make_shared;
 using std::pair;
 using std::shared_ptr;
 using std::string;
-using std::vector;
 
 
 VideoFilterGraph::VideoFilterGraph (dcp::Size s, AVPixelFormat p, dcp::Fraction r)
@@ -59,7 +58,7 @@ VideoFilterGraph::process (AVFrame* frame)
        list<pair<shared_ptr<Image>, int64_t>> images;
 
        if (_copy) {
-               images.push_back (make_pair(make_shared<Image>(frame), frame->best_effort_timestamp));
+               images.push_back (make_pair(make_shared<Image>(frame, Image::Alignment::PADDED), frame->best_effort_timestamp));
        } else {
                int r = av_buffersrc_write_frame (_buffer_src_context, frame);
                if (r < 0) {
@@ -71,7 +70,7 @@ VideoFilterGraph::process (AVFrame* frame)
                                break;
                        }
 
-                       images.push_back (make_pair(make_shared<Image>(_frame), frame->best_effort_timestamp));
+                       images.push_back (make_pair(make_shared<Image>(_frame, Image::Alignment::PADDED), frame->best_effort_timestamp));
                        av_frame_unref (_frame);
                }
        }
index 7181ad12968b7de52d1ece39ea0e105891bfe669..e409b97311be1fa5c78a5952d6d5a20c51a2102b 100644 (file)
@@ -139,20 +139,9 @@ class DOMFrame : public wxFrame
 {
 public:
        DOMFrame ()
-               : wxFrame (0, -1, _("DCP-o-matic Player"))
-               , _dual_screen (0)
-               , _update_news_requested (false)
-               , _info (0)
+               : wxFrame (nullptr, -1, _("DCP-o-matic Player"))
                , _mode (Config::instance()->player_mode())
-               , _config_dialog (0)
-               , _file_menu (0)
-               , _history_items (0)
-               , _history_position (0)
-               , _history_separator (0)
-               , _system_information_dialog (0)
-               , _view_full_screen (0)
-               , _view_dual_screen (0)
-               , _main_sizer (new wxBoxSizer (wxVERTICAL))
+               , _main_sizer (new wxBoxSizer(wxVERTICAL))
        {
                dcpomatic_log = make_shared<NullLog>();
 
@@ -199,7 +188,7 @@ public:
                */
                _overall_panel = new wxPanel (this, wxID_ANY);
 
-               _viewer.reset (new FilmViewer (_overall_panel));
+               _viewer = make_shared<FilmViewer>(_overall_panel);
                if (Config::instance()->player_mode() == Config::PLAYER_MODE_DUAL) {
                        auto pc = new PlaylistControls (_overall_panel, _viewer);
                        _controls = pc;
@@ -208,6 +197,7 @@ public:
                        _controls = new StandardControls (_overall_panel, _viewer, false);
                }
                _viewer->set_dcp_decode_reduction (Config::instance()->decode_reduction ());
+               _viewer->set_optimise_for_j2k (true);
                _viewer->PlaybackPermitted.connect (bind(&DOMFrame::playback_permitted, this));
                _viewer->Started.connect (bind(&DOMFrame::playback_started, this, _1));
                _viewer->Stopped.connect (bind(&DOMFrame::playback_stopped, this, _1));
@@ -255,7 +245,7 @@ public:
 
                reset_film ();
 
-               UpdateChecker::instance()->StateChanged.connect (boost::bind (&DOMFrame::update_checker_state_changed, this));
+               UpdateChecker::instance()->StateChanged.connect (boost::bind(&DOMFrame::update_checker_state_changed, this));
                setup_screen ();
 
                _stress.LoadDCP.connect (boost::bind(&DOMFrame::load_dcp, this, _1));
@@ -1038,30 +1028,30 @@ private:
                _viewer->seek (_film->length() - _viewer->one_video_frame(), true);
        }
 
-       wxFrame* _dual_screen;
-       bool _update_news_requested;
-       PlayerInformation* _info;
+       wxFrame* _dual_screen = nullptr;
+       bool _update_news_requested = false;
+       PlayerInformation* _info = nullptr;
        Config::PlayerMode _mode;
-       wxPreferencesEditor* _config_dialog;
-       wxPanel* _overall_panel;
-       wxMenu* _file_menu;
-       wxMenuItem* _view_cpl;
-       wxMenu* _cpl_menu;
-       int _history_items;
-       int _history_position;
-       wxMenuItem* _history_separator;
+       wxPreferencesEditor* _config_dialog = nullptr;
+       wxPanel* _overall_panel = nullptr;
+       wxMenu* _file_menu = nullptr;
+       wxMenuItem* _view_cpl = nullptr;
+       wxMenu* _cpl_menu = nullptr;
+       int _history_items = 0;
+       int _history_position = 0;
+       wxMenuItem* _history_separator = nullptr;
        shared_ptr<FilmViewer> _viewer;
        Controls* _controls;
-       SystemInformationDialog* _system_information_dialog;
+       SystemInformationDialog* _system_information_dialog = nullptr;
        std::shared_ptr<Film> _film;
        boost::signals2::scoped_connection _config_changed_connection;
        boost::signals2::scoped_connection _examine_job_connection;
-       wxMenuItem* _file_add_ov;
-       wxMenuItem* _file_add_kdm;
-       wxMenuItem* _tools_verify;
-       wxMenuItem* _view_full_screen;
-       wxMenuItem* _view_dual_screen;
-       wxSizer* _main_sizer;
+       wxMenuItem* _file_add_ov = nullptr;
+       wxMenuItem* _file_add_kdm = nullptr;
+       wxMenuItem* _tools_verify = nullptr;
+       wxMenuItem* _view_full_screen = nullptr;
+       wxMenuItem* _view_dual_screen = nullptr;
+       wxSizer* _main_sizer = nullptr;
        PlayerStressTester _stress;
 };
 
@@ -1106,7 +1096,6 @@ class App : public wxApp
 public:
        App ()
                : wxApp ()
-               , _frame (0)
        {
 #ifdef DCPOMATIC_LINUX
                XInitThreads ();
@@ -1117,7 +1106,7 @@ private:
 
        bool OnInit ()
        {
-               wxSplashScreen* splash = 0;
+               wxSplashScreen* splash = nullptr;
                try {
                        wxInitAllImageHandlers ();
 
@@ -1167,7 +1156,7 @@ private:
                        _frame->Maximize ();
                        if (splash) {
                                splash->Destroy ();
-                               splash = 0;
+                               splash = nullptr;
                        }
                        _frame->Show ();
 
@@ -1292,7 +1281,7 @@ private:
                message_dialog (_frame, std_to_wx (m));
        }
 
-       DOMFrame* _frame;
+       DOMFrame* _frame = nullptr;
        string _dcp_to_load;
        boost::optional<string> _stress;
 };
index 6c7371253fc3df789342bd5e1961dd57049457a9..ff3295599da1ca4e905cd922cbfd4b18d49d1dcc 100644 (file)
@@ -155,7 +155,7 @@ main (int argc, char* argv[])
                film = make_shared<Film>(film_dir);
                film->read_metadata ();
 
-               auto player = make_shared<Player>(film);
+               auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
                player->Video.connect (bind(&process_video, _1));
                while (!player->pass ()) {}
        } catch (std::exception& e) {
index 700769a50c7543cdaee030223128facda4541323..29f4aeaa364e2416de48dda239f25e2ebbd8ee27 100644 (file)
@@ -144,13 +144,13 @@ Controls::Controls (wxWindow* parent, shared_ptr<FilmViewer> viewer, bool editor
                _jump_to_selected->SetValue (Config::instance()->jump_to_selected ());
        }
 
-       _viewer->Started.connect (boost::bind(&Controls::started, this));
-       _viewer->Stopped.connect (boost::bind(&Controls::stopped, this));
+       viewer->Started.connect (boost::bind(&Controls::started, this));
+       viewer->Stopped.connect (boost::bind(&Controls::stopped, this));
 
        Bind (wxEVT_TIMER, boost::bind(&Controls::update_position, this));
        _timer.Start (80, wxTIMER_CONTINUOUS);
 
-       set_film (_viewer->film());
+       set_film (viewer->film());
 
        setup_sensitivity ();
 
@@ -186,7 +186,12 @@ Controls::stopped ()
 void
 Controls::update_position ()
 {
-       if (!_slider_being_moved && !_viewer->pending_idle_get()) {
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
+       if (!_slider_being_moved && !viewer->pending_idle_get()) {
                update_position_label ();
                update_position_slider ();
        }
@@ -196,14 +201,24 @@ Controls::update_position ()
 void
 Controls::eye_changed ()
 {
-       _viewer->set_eyes (_eye->GetSelection() == 0 ? Eyes::LEFT : Eyes::RIGHT);
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
+       viewer->set_eyes (_eye->GetSelection() == 0 ? Eyes::LEFT : Eyes::RIGHT);
 }
 
 
 void
 Controls::outline_content_changed ()
 {
-       _viewer->set_outline_content (_outline_content->GetValue());
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
+       viewer->set_outline_content (_outline_content->GetValue());
 }
 
 
@@ -211,13 +226,14 @@ Controls::outline_content_changed ()
 void
 Controls::slider_moved (bool page)
 {
-       if (!_film) {
+       auto viewer = _viewer.lock ();
+       if (!_film || !viewer) {
                return;
        }
 
        if (!page && !_slider_being_moved) {
                /* This is the first event of a drag; stop playback for the duration of the drag */
-               _viewer->suspend ();
+               viewer->suspend ();
                _slider_being_moved = true;
        }
 
@@ -228,10 +244,10 @@ Controls::slider_moved (bool page)
        */
        bool accurate = false;
        if (t >= _film->length ()) {
-               t = _film->length() - _viewer->one_video_frame();
+               t = _film->length() - viewer->one_video_frame();
                accurate = true;
        }
-       _viewer->seek (t, accurate);
+       viewer->seek (t, accurate);
        update_position_label ();
 
        log (
@@ -245,8 +261,13 @@ Controls::slider_moved (bool page)
 void
 Controls::slider_released ()
 {
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
        /* Restart after a drag */
-       _viewer->resume ();
+       viewer->resume ();
        _slider_being_moved = false;
 }
 
@@ -259,10 +280,15 @@ Controls::update_position_slider ()
                return;
        }
 
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
        auto const len = _film->length ();
 
        if (len.get ()) {
-               int const new_slider_position = 4096 * _viewer->position().get() / len.get();
+               int const new_slider_position = 4096 * viewer->position().get() / len.get();
                if (new_slider_position != _slider->GetValue()) {
                        _slider->SetValue (new_slider_position);
                }
@@ -279,10 +305,15 @@ Controls::update_position_label ()
                return;
        }
 
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
        double const fps = _film->video_frame_rate ();
        /* Count frame number from 1 ... not sure if this is the best idea */
-       checked_set (_frame_number, wxString::Format (wxT("%ld"), lrint (_viewer->position().seconds() * fps) + 1));
-       checked_set (_timecode, time_to_timecode (_viewer->position(), fps));
+       checked_set (_frame_number, wxString::Format (wxT("%ld"), lrint (viewer->position().seconds() * fps) + 1));
+       checked_set (_timecode, time_to_timecode (viewer->position(), fps));
 }
 
 
@@ -297,7 +328,12 @@ Controls::active_jobs_changed (optional<string> j)
 DCPTime
 Controls::nudge_amount (wxKeyboardState& ev)
 {
-       auto amount = _viewer->one_video_frame ();
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return {};
+       }
+
+       auto amount = viewer->one_video_frame ();
 
        if (ev.ShiftDown() && !ev.ControlDown()) {
                amount = DCPTime::from_seconds (1);
@@ -314,7 +350,10 @@ Controls::nudge_amount (wxKeyboardState& ev)
 void
 Controls::rewind_clicked (wxMouseEvent& ev)
 {
-       _viewer->seek (DCPTime(), true);
+       auto viewer = _viewer.lock ();
+       if (viewer) {
+               viewer->seek (DCPTime(), true);
+       }
        ev.Skip();
 }
 
@@ -322,28 +361,40 @@ Controls::rewind_clicked (wxMouseEvent& ev)
 void
 Controls::back_frame ()
 {
-       _viewer->seek_by (-_viewer->one_video_frame(), true);
+       auto viewer = _viewer.lock ();
+       if (viewer) {
+               viewer->seek_by (-viewer->one_video_frame(), true);
+       }
 }
 
 
 void
 Controls::forward_frame ()
 {
-       _viewer->seek_by (_viewer->one_video_frame(), true);
+       auto viewer = _viewer.lock ();
+       if (viewer) {
+               viewer->seek_by (viewer->one_video_frame(), true);
+       }
 }
 
 
 void
 Controls::back_clicked (wxKeyboardState& ev)
 {
-       _viewer->seek_by (-nudge_amount(ev), true);
+       auto viewer = _viewer.lock ();
+       if (viewer) {
+               viewer->seek_by (-nudge_amount(ev), true);
+       }
 }
 
 
 void
 Controls::forward_clicked (wxKeyboardState& ev)
 {
-       _viewer->seek_by (nudge_amount(ev), true);
+       auto viewer = _viewer.lock ();
+       if (viewer) {
+               viewer->seek_by (nudge_amount(ev), true);
+       }
 }
 
 
@@ -376,9 +427,14 @@ Controls::setup_sensitivity ()
 void
 Controls::timecode_clicked ()
 {
-       auto dialog = new PlayheadToTimecodeDialog (this, _viewer->position(), _film->video_frame_rate());
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
+       auto dialog = new PlayheadToTimecodeDialog (this, viewer->position(), _film->video_frame_rate());
        if (dialog->ShowModal() == wxID_OK) {
-               _viewer->seek (dialog->get(), true);
+               viewer->seek (dialog->get(), true);
        }
        dialog->Destroy ();
 }
@@ -387,9 +443,14 @@ Controls::timecode_clicked ()
 void
 Controls::frame_number_clicked ()
 {
-       auto dialog = new PlayheadToFrameDialog (this, _viewer->position(), _film->video_frame_rate());
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
+       auto dialog = new PlayheadToFrameDialog (this, viewer->position(), _film->video_frame_rate());
        if (dialog->ShowModal() == wxID_OK) {
-               _viewer->seek (dialog->get(), true);
+               viewer->seek (dialog->get(), true);
        }
        dialog->Destroy ();
 }
index 9a22d770908ae6d6e581fbce6427b59c4389ba72..377960425ba2807e5982ede7f25ffd4ec8e376a5 100644 (file)
@@ -79,7 +79,7 @@ protected:
        wxBoxSizer* _button_sizer;
        std::shared_ptr<Film> _film;
        wxSlider* _slider;
-       std::shared_ptr<FilmViewer> _viewer;
+       std::weak_ptr<FilmViewer> _viewer;
        boost::optional<std::string> _active_job;
 
 private:
index c7e154fa53597bf340bb4337cc32af6174bb8159..0131aa294a66e9caa6fa5841eeb3003e929c268c 100644 (file)
@@ -63,17 +63,11 @@ extern "C" {
 using std::bad_alloc;
 using std::cout;
 using std::dynamic_pointer_cast;
-using std::exception;
-using std::list;
-using std::make_pair;
 using std::make_shared;
 using std::max;
-using std::min;
-using std::pair;
 using std::shared_ptr;
 using std::string;
 using std::vector;
-using std::weak_ptr;
 using boost::optional;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
@@ -96,10 +90,10 @@ FilmViewer::FilmViewer (wxWindow* p)
 {
        switch (Config::instance()->video_view_type()) {
        case Config::VIDEO_VIEW_OPENGL:
-               _video_view = new GLVideoView (this, p);
+               _video_view = std::make_shared<GLVideoView>(this, p);
                break;
        case Config::VIDEO_VIEW_SIMPLE:
-               _video_view = new SimpleVideoView (this, p);
+               _video_view = std::make_shared<SimpleVideoView>(this, p);
                break;
        }
 
@@ -169,7 +163,7 @@ FilmViewer::set_film (shared_ptr<Film> film)
        }
 
        try {
-               _player = make_shared<Player>(_film);
+               _player = make_shared<Player>(_film, _optimise_for_j2k ? Image::Alignment::COMPACT : Image::Alignment::PADDED);
                _player->set_fast ();
                if (_dcp_decode_reduction) {
                        _player->set_dcp_decode_reduction (_dcp_decode_reduction);
@@ -214,6 +208,8 @@ FilmViewer::recreate_butler ()
                return;
        }
 
+       auto const j2k_gl_optimised = dynamic_pointer_cast<GLVideoView>(_video_view) && _optimise_for_j2k;
+
        _butler = std::make_shared<Butler>(
                _film,
                _player,
@@ -221,8 +217,9 @@ FilmViewer::recreate_butler ()
                _audio_channels,
                bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24),
                VideoRange::FULL,
-               false,
-               true
+               j2k_gl_optimised ? Image::Alignment::COMPACT : Image::Alignment::PADDED,
+               true,
+               j2k_gl_optimised
                );
 
        if (!Config::instance()->sound() && !_audio.isStreamOpen()) {
@@ -281,21 +278,22 @@ FilmViewer::calculate_sizes ()
        auto const view_ratio = float(_video_view->get()->GetSize().x) / _video_view->get()->GetSize().y;
        auto const film_ratio = container ? container->ratio () : 1.78;
 
+       dcp::Size out_size;
        if (view_ratio < film_ratio) {
                /* panel is less widscreen than the film; clamp width */
-               _out_size.width = _video_view->get()->GetSize().x;
-               _out_size.height = lrintf (_out_size.width / film_ratio);
+               out_size.width = _video_view->get()->GetSize().x;
+               out_size.height = lrintf (out_size.width / film_ratio);
        } else {
                /* panel is more widescreen than the film; clamp height */
-               _out_size.height = _video_view->get()->GetSize().y;
-               _out_size.width = lrintf (_out_size.height * film_ratio);
+               out_size.height = _video_view->get()->GetSize().y;
+               out_size.width = lrintf (out_size.height * film_ratio);
        }
 
        /* Catch silly values */
-       _out_size.width = max (64, _out_size.width);
-       _out_size.height = max (64, _out_size.height);
+       out_size.width = max (64, out_size.width);
+       out_size.height = max (64, out_size.height);
 
-       _player->set_video_container_size (_out_size);
+       _player->set_video_container_size (out_size);
 }
 
 
@@ -771,3 +769,12 @@ FilmViewer::image_changed (shared_ptr<PlayerVideo> pv)
 {
        emit (boost::bind(boost::ref(ImageChanged), pv));
 }
+
+
+void
+FilmViewer::set_optimise_for_j2k (bool o)
+{
+       _optimise_for_j2k = o;
+       _video_view->set_optimise_for_j2k (o);
+}
+
index 2efe448c9e4deeaa37ba58e73e1511cf3ae48ee8..5e5bb791660c0ebfe4d295adf03cfb0c1508908b 100644 (file)
@@ -62,7 +62,7 @@ public:
                return _video_view->get();
        }
 
-       VideoView const * video_view () const {
+       std::shared_ptr<const VideoView> video_view () const {
                return _video_view;
        }
 
@@ -98,6 +98,7 @@ public:
        void set_outline_subtitles (boost::optional<dcpomatic::Rect<double>>);
        void set_eyes (Eyes e);
        void set_pad_black (bool p);
+       void set_optimise_for_j2k (bool o);
 
        void slow_refresh ();
 
@@ -115,9 +116,6 @@ public:
        }
 
        /* Some accessors and utility methods that VideoView classes need */
-       dcp::Size out_size () const {
-               return _out_size;
-       }
        bool outline_content () const {
                return _outline_content;
        }
@@ -172,13 +170,10 @@ private:
        std::shared_ptr<Film> _film;
        std::shared_ptr<Player> _player;
 
-       VideoView* _video_view = nullptr;
+       std::shared_ptr<VideoView> _video_view;
        bool _coalesce_player_changes = false;
        std::vector<int> _pending_player_changes;
 
-       /** Size of our output (including padding if we have any) */
-       dcp::Size _out_size;
-
        RtAudio _audio;
        int _audio_channels = 0;
        unsigned int _audio_block_size = 1024;
@@ -193,6 +188,11 @@ private:
 
        boost::optional<int> _dcp_decode_reduction;
 
+       /** true to assume that this viewer is only being used for JPEG2000 sources
+        *  so it can optimise accordingly.
+        */
+       bool _optimise_for_j2k = false;
+
        ClosedCaptionsDialog* _closed_captions_dialog = nullptr;
 
        bool _outline_content = false;
index 7bf9e3adc6d8e880403a601f5b268c6e21bffeba..04e0bac46ec373e3b731768fa00b949f5290505a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2018-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
+#ifdef DCPOMATIC_WINDOWS
+#include <GL/glew.h>
+#endif
+
 #include "gl_video_view.h"
 #include "film_viewer.h"
 #include "wx_util.h"
 #include <iostream>
 
 #ifdef DCPOMATIC_OSX
-#include <OpenGL/glu.h>
-#include <OpenGL/glext.h>
-#include <OpenGL/CGLTypes.h>
+#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED
 #include <OpenGL/OpenGL.h>
+#include <OpenGL/gl3.h>
 #endif
 
 #ifdef DCPOMATIC_LINUX
 
 #ifdef DCPOMATIC_WINDOWS
 #include <GL/glu.h>
-#include <GL/glext.h>
 #include <GL/wglext.h>
 #endif
 
+
 using std::cout;
 using std::shared_ptr;
+using std::string;
 using boost::optional;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
@@ -69,12 +74,19 @@ check_gl_error (char const * last)
 GLVideoView::GLVideoView (FilmViewer* viewer, wxWindow *parent)
        : VideoView (viewer)
        , _context (nullptr)
-       , _have_storage (false)
        , _vsync_enabled (false)
        , _playing (false)
        , _one_shot (false)
 {
-       _canvas = new wxGLCanvas (parent, wxID_ANY, 0, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE);
+       wxGLAttributes attributes;
+       /* We don't need a depth buffer, and indeed there is apparently a bug with Windows/Intel HD 630
+        * which puts green lines over the OpenGL display if you have a non-zero depth buffer size.
+        * https://community.intel.com/t5/Graphics/Request-for-details-on-Intel-HD-630-green-lines-in-OpenGL-apps/m-p/1202179
+        */
+       attributes.PlatformDefaults().MinRGBA(8, 8, 8, 8).DoubleBuffer().Depth(0).EndList();
+       _canvas = new wxGLCanvas (
+               parent, attributes, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE
+       );
        _canvas->Bind (wxEVT_PAINT, boost::bind(&GLVideoView::update, this));
        _canvas->Bind (wxEVT_SIZE, boost::bind(&GLVideoView::size_changed, this, _1));
 
@@ -101,8 +113,6 @@ GLVideoView::~GLVideoView ()
                _thread.interrupt ();
                _thread.join ();
        } catch (...) {}
-
-       glDeleteTextures (1, &_id);
 }
 
 void
@@ -130,9 +140,18 @@ GLVideoView::update ()
                return;
        }
 
+       /* It appears important to do this from the GUI thread; if we do it from the GL thread
+        * on Linux we get strange failures to create the context for any version of GL higher
+        * than 3.2.
+        */
+       ensure_context ();
+
 #ifdef DCPOMATIC_OSX
        /* macOS gives errors if we don't do this (and therefore [NSOpenGLContext setView:]) from the main thread */
-       ensure_context ();
+       if (!_setup_shaders_done) {
+               setup_shaders ();
+               _setup_shaders_done = true;
+       }
 #endif
 
        if (!_thread.joinable()) {
@@ -140,34 +159,307 @@ GLVideoView::update ()
        }
 
        request_one_shot ();
+
+       rethrow ();
 }
 
 
+static constexpr char vertex_source[] =
+"#version 330 core\n"
+"\n"
+"layout (location = 0) in vec3 in_pos;\n"
+"layout (location = 1) in vec2 in_tex_coord;\n"
+"\n"
+"out vec2 TexCoord;\n"
+"\n"
+"void main()\n"
+"{\n"
+"      gl_Position = vec4(in_pos, 1.0);\n"
+"      TexCoord = in_tex_coord;\n"
+"}\n";
+
+
+/* Bicubic interpolation stolen from https://stackoverflow.com/questions/13501081/efficient-bicubic-filtering-code-in-glsl */
+static constexpr char fragment_source[] =
+"#version 330 core\n"
+"\n"
+"in vec2 TexCoord;\n"
+"\n"
+"uniform sampler2D texture_sampler;\n"
+/* type = 0: draw border
+ * type = 1: draw XYZ image
+ * type = 2: draw RGB image
+ */
+"uniform int type = 0;\n"
+"uniform vec4 border_colour;\n"
+"uniform mat4 colour_conversion;\n"
+"\n"
+"out vec4 FragColor;\n"
+"\n"
+"vec4 cubic(float x)\n"
+"\n"
+"#define IN_GAMMA 2.2\n"
+"#define OUT_GAMMA 0.384615385\n"       //  1 /  2.6
+"#define DCI_COEFFICIENT 0.91655528\n"  // 48 / 53.37
+"\n"
+"{\n"
+"    float x2 = x * x;\n"
+"    float x3 = x2 * x;\n"
+"    vec4 w;\n"
+"    w.x =     -x3 + 3 * x2 - 3 * x + 1;\n"
+"    w.y =  3 * x3 - 6 * x2         + 4;\n"
+"    w.z = -3 * x3 + 3 * x2 + 3 * x + 1;\n"
+"    w.w =  x3;\n"
+"    return w / 6.f;\n"
+"}\n"
+"\n"
+"vec4 texture_bicubic(sampler2D sampler, vec2 tex_coords)\n"
+"{\n"
+"   vec2 tex_size = textureSize(sampler, 0);\n"
+"   vec2 inv_tex_size = 1.0 / tex_size;\n"
+"\n"
+"   tex_coords = tex_coords * tex_size - 0.5;\n"
+"\n"
+"   vec2 fxy = fract(tex_coords);\n"
+"   tex_coords -= fxy;\n"
+"\n"
+"   vec4 xcubic = cubic(fxy.x);\n"
+"   vec4 ycubic = cubic(fxy.y);\n"
+"\n"
+"   vec4 c = tex_coords.xxyy + vec2 (-0.5, +1.5).xyxy;\n"
+"\n"
+"   vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw);\n"
+"   vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s;\n"
+"\n"
+"   offset *= inv_tex_size.xxyy;\n"
+"\n"
+"   vec4 sample0 = texture(sampler, offset.xz);\n"
+"   vec4 sample1 = texture(sampler, offset.yz);\n"
+"   vec4 sample2 = texture(sampler, offset.xw);\n"
+"   vec4 sample3 = texture(sampler, offset.yw);\n"
+"\n"
+"   float sx = s.x / (s.x + s.y);\n"
+"   float sy = s.z / (s.z + s.w);\n"
+"\n"
+"   return mix(\n"
+"         mix(sample3, sample2, sx), mix(sample1, sample0, sx)\n"
+"         , sy);\n"
+"}\n"
+"\n"
+"void main()\n"
+"{\n"
+"      switch (type) {\n"
+"              case 0:\n"
+"                      FragColor = border_colour;\n"
+"                      break;\n"
+"              case 1:\n"
+"                      FragColor = texture_bicubic(texture_sampler, TexCoord);\n"
+"                      FragColor.x = pow(FragColor.x, IN_GAMMA) / DCI_COEFFICIENT;\n"
+"                      FragColor.y = pow(FragColor.y, IN_GAMMA) / DCI_COEFFICIENT;\n"
+"                      FragColor.z = pow(FragColor.z, IN_GAMMA) / DCI_COEFFICIENT;\n"
+"                      FragColor = colour_conversion * FragColor;\n"
+"                      FragColor.x = pow(FragColor.x, OUT_GAMMA);\n"
+"                      FragColor.y = pow(FragColor.y, OUT_GAMMA);\n"
+"                      FragColor.z = pow(FragColor.z, OUT_GAMMA);\n"
+"                      break;\n"
+"              case 2:\n"
+"                      FragColor = texture_bicubic(texture_sampler, TexCoord);\n"
+"                      break;\n"
+"      }\n"
+"}\n";
+
+
 void
 GLVideoView::ensure_context ()
 {
        if (!_context) {
-               _context = new wxGLContext (_canvas);
-               _canvas->SetCurrent (*_context);
+               wxGLContextAttrs attrs;
+               attrs.PlatformDefaults().CoreProfile().OGLVersion(4, 1).EndList();
+               _context = new wxGLContext (_canvas, nullptr, &attrs);
+               if (!_context->IsOK()) {
+                       throw GLError ("Making GL context", -1);
+               }
        }
 }
 
+
+/* Offset of video texture triangles in indices */
+static constexpr int indices_video_texture = 0;
+/* Offset of subtitle texture triangles in indices */
+static constexpr int indices_subtitle_texture = 6;
+/* Offset of border lines in indices */
+static constexpr int indices_border = 12;
+
+static constexpr unsigned int indices[] = {
+       0, 1, 3, // video texture triangle #1
+       1, 2, 3, // video texture triangle #2
+       4, 5, 7, // subtitle texture triangle #1
+       5, 6, 7, // subtitle texture triangle #2
+       8, 9,    // border line #1
+       9, 10,   // border line #2
+       10, 11,  // border line #3
+       11, 8,   // border line #4
+};
+
+
 void
-GLVideoView::draw (Position<int> inter_position, dcp::Size inter_size)
+GLVideoView::setup_shaders ()
 {
-       glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-       check_gl_error ("glClear");
+       DCPOMATIC_ASSERT (_canvas);
+       DCPOMATIC_ASSERT (_context);
+       auto r = _canvas->SetCurrent (*_context);
+       DCPOMATIC_ASSERT (r);
+
+#ifdef DCPOMATIC_WINDOWS
+       r = glewInit();
+       if (r != GLEW_OK) {
+               throw GLError(reinterpret_cast<char const*>(glewGetErrorString(r)));
+       }
+#endif
 
-       glClearColor (0.0f, 0.0f, 0.0f, 1.0f);
-       check_gl_error ("glClearColor");
-       glEnable (GL_TEXTURE_2D);
-       check_gl_error ("glEnable GL_TEXTURE_2D");
+       auto get_information = [this](GLenum name) {
+               auto s = glGetString (name);
+               if (s) {
+                       _information[name] = std::string (reinterpret_cast<char const *>(s));
+               }
+       };
+
+       get_information (GL_VENDOR);
+       get_information (GL_RENDERER);
+       get_information (GL_VERSION);
+       get_information (GL_SHADING_LANGUAGE_VERSION);
+
+       glGenVertexArrays(1, &_vao);
+       check_gl_error ("glGenVertexArrays");
+       GLuint vbo;
+       glGenBuffers(1, &vbo);
+       check_gl_error ("glGenBuffers");
+       GLuint ebo;
+       glGenBuffers(1, &ebo);
+       check_gl_error ("glGenBuffers");
+
+       glBindVertexArray(_vao);
+       check_gl_error ("glBindVertexArray");
+
+       glBindBuffer(GL_ARRAY_BUFFER, vbo);
+       check_gl_error ("glBindBuffer");
+
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
+       check_gl_error ("glBindBuffer");
+       glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
+       check_gl_error ("glBufferData");
+
+       /* position attribute to vertex shader (location = 0) */
+       glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), nullptr);
+       glEnableVertexAttribArray(0);
+       /* texture coord attribute to vertex shader (location = 1) */
+       glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float)));
+       glEnableVertexAttribArray(1);
+       check_gl_error ("glEnableVertexAttribArray");
+
+       auto compile = [](GLenum type, char const* source) -> GLuint {
+               auto shader = glCreateShader(type);
+               DCPOMATIC_ASSERT (shader);
+               GLchar const * src[] = { static_cast<GLchar const *>(source) };
+               glShaderSource(shader, 1, src, nullptr);
+               check_gl_error ("glShaderSource");
+               glCompileShader(shader);
+               check_gl_error ("glCompileShader");
+               GLint ok;
+               glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
+               if (!ok) {
+                       GLint log_length;
+                       glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+                       string log;
+                       if (log_length > 0) {
+                               char* log_char = new char[log_length];
+                               glGetShaderInfoLog(shader, log_length, nullptr, log_char);
+                               log = string(log_char);
+                               delete[] log_char;
+                       }
+                       glDeleteShader(shader);
+                       throw GLError(String::compose("Could not compile shader (%1)", log).c_str(), -1);
+               }
+               return shader;
+       };
+
+       auto vertex_shader = compile (GL_VERTEX_SHADER, vertex_source);
+       auto fragment_shader = compile (GL_FRAGMENT_SHADER, fragment_source);
+
+       auto program = glCreateProgram();
+       check_gl_error ("glCreateProgram");
+       glAttachShader (program, vertex_shader);
+       check_gl_error ("glAttachShader");
+       glAttachShader (program, fragment_shader);
+       check_gl_error ("glAttachShader");
+       glLinkProgram (program);
+       check_gl_error ("glLinkProgram");
+       GLint ok;
+       glGetProgramiv (program, GL_LINK_STATUS, &ok);
+       if (!ok) {
+               GLint log_length;
+               glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+               string log;
+               if (log_length > 0) {
+                       char* log_char = new char[log_length];
+                       glGetProgramInfoLog(program, log_length, nullptr, log_char);
+                       log = string(log_char);
+                       delete[] log_char;
+               }
+               glDeleteProgram (program);
+               throw GLError(String::compose("Could not link shader (%1)", log).c_str(), -1);
+       }
+       glDeleteShader (vertex_shader);
+       glDeleteShader (fragment_shader);
+
+       glUseProgram (program);
+
+       _fragment_type = glGetUniformLocation (program, "type");
+       check_gl_error ("glGetUniformLocation");
+       set_border_colour (program);
+
+       auto conversion = dcp::ColourConversion::rec709_to_xyz();
+       boost::numeric::ublas::matrix<double> matrix = conversion.xyz_to_rgb ();
+       GLfloat gl_matrix[] = {
+               static_cast<float>(matrix(0, 0)), static_cast<float>(matrix(0, 1)), static_cast<float>(matrix(0, 2)), 0.0f,
+               static_cast<float>(matrix(1, 0)), static_cast<float>(matrix(1, 1)), static_cast<float>(matrix(1, 2)), 0.0f,
+               static_cast<float>(matrix(2, 0)), static_cast<float>(matrix(2, 1)), static_cast<float>(matrix(2, 2)), 0.0f,
+               0.0f, 0.0f, 0.0f, 1.0f
+               };
+
+       auto colour_conversion = glGetUniformLocation (program, "colour_conversion");
+       check_gl_error ("glGetUniformLocation");
+       glUniformMatrix4fv (colour_conversion, 1, GL_TRUE, gl_matrix);
+
+       glLineWidth (2.0f);
        glEnable (GL_BLEND);
-       check_gl_error ("glEnable GL_BLEND");
-       glDisable (GL_DEPTH_TEST);
-       check_gl_error ("glDisable GL_DEPTH_TEST");
        glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
+       /* Reserve space for the GL_ARRAY_BUFFER */
+       glBufferData(GL_ARRAY_BUFFER, 12 * 5 * sizeof(float), nullptr, GL_STATIC_DRAW);
+       check_gl_error ("glBufferData");
+}
+
+
+void
+GLVideoView::set_border_colour (GLuint program)
+{
+       auto uniform = glGetUniformLocation (program, "border_colour");
+       check_gl_error ("glGetUniformLocation");
+       auto colour = outline_content_colour ();
+       glUniform4f (uniform, colour.Red() / 255.0f, colour.Green() / 255.0f, colour.Blue() / 255.0f, 1.0f);
+       check_gl_error ("glUniform4f");
+}
+
+
+void
+GLVideoView::draw (Position<int>, dcp::Size)
+{
+       auto pad = pad_colour();
+       glClearColor(pad.Red() / 255.0, pad.Green() / 255.0, pad.Blue() / 255.0, 1.0);
+       glClear (GL_COLOR_BUFFER_BIT);
+       check_gl_error ("glClear");
+
        auto const size = _canvas_size.load();
        int const width = size.GetWidth();
        int const height = size.GetHeight();
@@ -178,83 +470,21 @@ GLVideoView::draw (Position<int> inter_position, dcp::Size inter_size)
 
        glViewport (0, 0, width, height);
        check_gl_error ("glViewport");
-       glMatrixMode (GL_PROJECTION);
-       glLoadIdentity ();
-
-DCPOMATIC_DISABLE_WARNINGS
-       gluOrtho2D (0, width, height, 0);
-DCPOMATIC_ENABLE_WARNINGS
-       check_gl_error ("gluOrtho2d");
-       glMatrixMode (GL_MODELVIEW);
-       glLoadIdentity ();
-
-       glTranslatef (0, 0, 0);
-
-       dcp::Size const out_size = _viewer->out_size ();
-
-       if (_size) {
-               /* Render our image (texture) */
-               glBegin (GL_QUADS);
-               glTexCoord2f (0, 1);
-               glVertex2f (0, _size->height);
-               glTexCoord2f (1, 1);
-               glVertex2f (_size->width, _size->height);
-               glTexCoord2f (1, 0);
-               glVertex2f (_size->width, 0);
-               glTexCoord2f (0, 0);
-               glVertex2f (0, 0);
-               glEnd ();
-       } else {
-               /* No image, so just fill with black */
-               glBegin (GL_QUADS);
-               glColor3ub (0, 0, 0);
-               glVertex2f (0, 0);
-               glVertex2f (out_size.width, 0);
-               glVertex2f (out_size.width, out_size.height);
-               glVertex2f (0, out_size.height);
-               glVertex2f (0, 0);
-               glEnd ();
-       }
 
-       if (!_viewer->pad_black() && out_size.width < width) {
-               glBegin (GL_QUADS);
-               /* XXX: these colours are right for GNOME; may need adjusting for other OS */
-               glColor3ub (240, 240, 240);
-               glVertex2f (out_size.width, 0);
-               glVertex2f (width, 0);
-               glVertex2f (width, height);
-               glVertex2f (out_size.width, height);
-               glEnd ();
-               glColor3ub (255, 255, 255);
+       glBindVertexArray(_vao);
+       check_gl_error ("glBindVertexArray");
+       glUniform1i(_fragment_type, _optimise_for_j2k ? 1 : 2);
+       _video_texture->bind();
+       glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_video_texture * sizeof(int)));
+       if (_have_subtitle_to_render) {
+               glUniform1i(_fragment_type, 2);
+               _subtitle_texture->bind();
+               glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_subtitle_texture * sizeof(int)));
        }
-
-       if (!_viewer->pad_black() && out_size.height < height) {
-               glColor3ub (240, 240, 240);
-               int const gap = (height - out_size.height) / 2;
-               glBegin (GL_QUADS);
-               glVertex2f (0, 0);
-               glVertex2f (width, 0);
-               glVertex2f (width, gap);
-               glVertex2f (0, gap);
-               glEnd ();
-               glBegin (GL_QUADS);
-               glVertex2f (0, gap + out_size.height + 1);
-               glVertex2f (width, gap + out_size.height + 1);
-               glVertex2f (width, 2 * gap + out_size.height + 2);
-               glVertex2f (0, 2 * gap + out_size.height + 2);
-               glEnd ();
-               glColor3ub (255, 255, 255);
-       }
-
        if (_viewer->outline_content()) {
-               glColor3ub (255, 0, 0);
-               glBegin (GL_LINE_LOOP);
-               glVertex2f (inter_position.x, inter_position.y + (height - out_size.height) / 2);
-               glVertex2f (inter_position.x + inter_size.width, inter_position.y + (height - out_size.height) / 2);
-               glVertex2f (inter_position.x + inter_size.width, inter_position.y + (height - out_size.height) / 2 + inter_size.height);
-               glVertex2f (inter_position.x, inter_position.y + (height - out_size.height) / 2 + inter_size.height);
-               glEnd ();
-               glColor3ub (255, 255, 255);
+               glUniform1i(_fragment_type, 0);
+               glDrawElements (GL_LINES, 8, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_border * sizeof(int)));
+               check_gl_error ("glDrawElements");
        }
 
        glFlush();
@@ -263,33 +493,109 @@ DCPOMATIC_ENABLE_WARNINGS
        _canvas->SwapBuffers();
 }
 
+
 void
-GLVideoView::set_image (shared_ptr<const Image> image)
+GLVideoView::set_image (shared_ptr<const PlayerVideo> pv)
 {
-       if (!image) {
-               _size = optional<dcp::Size>();
-               return;
+       shared_ptr<const Image> video = _optimise_for_j2k ? pv->raw_image() : pv->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, true);
+
+       /* Only the player's black frames should be aligned at this stage, so this should
+        * almost always have no work to do.
+        */
+       video = Image::ensure_alignment (video, Image::Alignment::COMPACT);
+
+       /** If _optimise_for_j2k is true we render a XYZ image, doing the colourspace
+        *  conversion, scaling and video range conversion in the GL shader.
+        *  Othewise we render a RGB image without any shader-side processing.
+        */
+
+       /* XXX: video range conversion */
+
+       _video_texture->set (video);
+
+       auto const text = pv->text();
+       _have_subtitle_to_render = static_cast<bool>(text) && _optimise_for_j2k;
+       if (_have_subtitle_to_render) {
+               /* opt: only do this if it's a new subtitle? */
+               DCPOMATIC_ASSERT (text->image->alignment() == Image::Alignment::COMPACT);
+               _subtitle_texture->set (text->image);
        }
 
-       DCPOMATIC_ASSERT (image->pixel_format() == AV_PIX_FMT_RGB24);
-       DCPOMATIC_ASSERT (!image->aligned());
 
-       if (image->size() != _size) {
-               _have_storage = false;
+       auto const canvas_size = _canvas_size.load();
+       int const canvas_width = canvas_size.GetWidth();
+       int const canvas_height = canvas_size.GetHeight();
+       auto inter_position = player_video().first->inter_position();
+       auto inter_size = player_video().first->inter_size();
+       auto out_size = player_video().first->out_size();
+
+       _last_canvas_size.set_next (canvas_size);
+       _last_video_size.set_next (video->size());
+       _last_inter_position.set_next (inter_position);
+       _last_inter_size.set_next (inter_size);
+       _last_out_size.set_next (out_size);
+
+       auto x_pixels_to_gl = [canvas_width](int x) {
+               return (x * 2.0f / canvas_width) - 1.0f;
+       };
+
+       auto y_pixels_to_gl = [canvas_height](int y) {
+               return (y * 2.0f / canvas_height) - 1.0f;
+       };
+
+       if (_last_canvas_size.changed() || _last_inter_position.changed() || _last_inter_size.changed() || _last_out_size.changed()) {
+               float const video_x1 = x_pixels_to_gl(_optimise_for_j2k ? inter_position.x : 0);
+               float const video_x2 = x_pixels_to_gl(_optimise_for_j2k ? (inter_position.x + inter_size.width) : out_size.width);
+               float const video_y1 = y_pixels_to_gl(_optimise_for_j2k ? inter_position.y : 0);
+               float const video_y2 = y_pixels_to_gl(_optimise_for_j2k ? (inter_position.y + inter_size.height) : out_size.height);
+               float video_vertices[] = {
+                        // positions              // texture coords
+                       video_x2, video_y2, 0.0f,  1.0f, 0.0f,   // video texture top right       (index 0)
+                       video_x2, video_y1, 0.0f,  1.0f, 1.0f,   // video texture bottom right    (index 1)
+                       video_x1, video_y1, 0.0f,  0.0f, 1.0f,   // video texture bottom left     (index 2)
+                       video_x1, video_y2, 0.0f,  0.0f, 0.0f,   // video texture top left        (index 3)
+               };
+
+               glBufferSubData (GL_ARRAY_BUFFER, 0, sizeof(video_vertices), video_vertices);
+               check_gl_error ("glBufferSubData (video)");
+
+               float const border_x1 = x_pixels_to_gl(inter_position.x);
+               float const border_y1 = y_pixels_to_gl(inter_position.y);
+               float const border_x2 = x_pixels_to_gl(inter_position.x + inter_size.width);
+               float const border_y2 = y_pixels_to_gl(inter_position.y + inter_size.height);
+
+               float border_vertices[] = {
+                        // positions                 // texture coords
+                        border_x1, border_y1, 0.0f,  0.0f, 0.0f,   // border bottom left         (index 4)
+                        border_x1, border_y2, 0.0f,  0.0f, 0.0f,   // border top left            (index 5)
+                        border_x2, border_y2, 0.0f,  0.0f, 0.0f,   // border top right           (index 6)
+                        border_x2, border_y1, 0.0f,  0.0f, 0.0f,   // border bottom right        (index 7)
+               };
+
+               glBufferSubData (GL_ARRAY_BUFFER, 8 * 5 * sizeof(float), sizeof(border_vertices), border_vertices);
+               check_gl_error ("glBufferSubData (border)");
        }
 
-       _size = image->size ();
-       glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
-       check_gl_error ("glPixelStorei");
-       if (_have_storage) {
-               glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, _size->width, _size->height, GL_RGB, GL_UNSIGNED_BYTE, image->data()[0]);
-               check_gl_error ("glTexSubImage2D");
-       } else {
-               glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB8, _size->width, _size->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data()[0]);
-               _have_storage = true;
-               check_gl_error ("glTexImage2D");
+       if (_have_subtitle_to_render) {
+               float const subtitle_x1 = x_pixels_to_gl(inter_position.x + text->position.x);
+               float const subtitle_x2 = x_pixels_to_gl(inter_position.x + text->position.x + text->image->size().width);
+               float const subtitle_y1 = y_pixels_to_gl(inter_position.y + text->position.y + text->image->size().height);
+               float const subtitle_y2 = y_pixels_to_gl(inter_position.y + text->position.y);
+
+               float vertices[] = {
+                        // positions                     // texture coords
+                        subtitle_x2, subtitle_y1, 0.0f,  1.0f, 0.0f,   // subtitle texture top right    (index 4)
+                        subtitle_x2, subtitle_y2, 0.0f,  1.0f, 1.0f,   // subtitle texture bottom right (index 5)
+                        subtitle_x1, subtitle_y2, 0.0f,  0.0f, 1.0f,   // subtitle texture bottom left  (index 6)
+                        subtitle_x1, subtitle_y1, 0.0f,  0.0f, 0.0f,   // subtitle texture top left     (index 7)
+               };
+
+               glBufferSubData (GL_ARRAY_BUFFER, 4 * 5 * sizeof(float), sizeof(vertices), vertices);
+               check_gl_error ("glBufferSubData (subtitle)");
        }
 
+       /* opt: where should these go? */
+
        glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        check_gl_error ("glTexParameteri");
@@ -299,6 +605,7 @@ GLVideoView::set_image (shared_ptr<const Image> image)
        check_gl_error ("glTexParameterf");
 }
 
+
 void
 GLVideoView::start ()
 {
@@ -321,7 +628,7 @@ void
 GLVideoView::thread_playing ()
 {
        if (length() != dcpomatic::DCPTime()) {
-               dcpomatic::DCPTime const next = position() + one_video_frame();
+               auto const next = position() + one_video_frame();
 
                if (next >= length()) {
                        _viewer->finished ();
@@ -346,9 +653,9 @@ GLVideoView::thread_playing ()
 void
 GLVideoView::set_image_and_draw ()
 {
-       shared_ptr<PlayerVideo> pv = player_video().first;
+       auto pv = player_video().first;
        if (pv) {
-               set_image (pv->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true));
+               set_image (pv);
                draw (pv->inter_position(), pv->inter_size());
                _viewer->image_changed (pv);
        }
@@ -367,12 +674,10 @@ try
         */
        WXGLSetCurrentContext (_context->GetWXGLContext());
 #else
-       /* We must call this here on Linux otherwise we get no image (for reasons
-        * that aren't clear).  However, doing ensure_context() from this thread
-        * on macOS gives
-        * "[NSOpenGLContext setView:] must be called from the main thread".
-        */
-       ensure_context ();
+       if (!_setup_shaders_done) {
+               setup_shaders ();
+               _setup_shaders_done = true;
+       }
 #endif
 
 #if defined(DCPOMATIC_LINUX) && defined(DCPOMATIC_HAVE_GLX_SWAP_INTERVAL_EXT)
@@ -403,12 +708,8 @@ try
        _vsync_enabled = true;
 #endif
 
-       glGenTextures (1, &_id);
-       check_gl_error ("glGenTextures");
-       glBindTexture (GL_TEXTURE_2D, _id);
-       check_gl_error ("glBindTexture");
-       glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
-       check_gl_error ("glPixelStorei");
+       _video_texture.reset(new Texture(_optimise_for_j2k ? 2 : 1));
+       _subtitle_texture.reset(new Texture(1));
 
        while (true) {
                boost::mutex::scoped_lock lm (_playing_mutex);
@@ -432,7 +733,7 @@ try
         * without also deleting the wxGLCanvas.
         */
 }
-catch (boost::thread_interrupted& e)
+catch (...)
 {
        store_current ();
 }
@@ -455,3 +756,77 @@ GLVideoView::request_one_shot ()
        _thread_work_condition.notify_all ();
 }
 
+
+Texture::Texture (GLint unpack_alignment)
+       : _unpack_alignment (unpack_alignment)
+{
+       glGenTextures (1, &_name);
+       check_gl_error ("glGenTextures");
+}
+
+
+Texture::~Texture ()
+{
+       glDeleteTextures (1, &_name);
+}
+
+
+void
+Texture::bind ()
+{
+       glBindTexture(GL_TEXTURE_2D, _name);
+       check_gl_error ("glBindTexture");
+}
+
+
+void
+Texture::set (shared_ptr<const Image> image)
+{
+       auto const create = !_size || image->size() != _size;
+       _size = image->size();
+
+       glPixelStorei (GL_UNPACK_ALIGNMENT, _unpack_alignment);
+       check_gl_error ("glPixelStorei");
+
+       DCPOMATIC_ASSERT (image->alignment() == Image::Alignment::COMPACT);
+
+       GLint internal_format;
+       GLenum format;
+       GLenum type;
+
+       switch (image->pixel_format()) {
+       case AV_PIX_FMT_BGRA:
+               internal_format = GL_RGBA8;
+               format = GL_BGRA;
+               type = GL_UNSIGNED_BYTE;
+               break;
+       case AV_PIX_FMT_RGBA:
+               internal_format = GL_RGBA8;
+               format = GL_RGBA;
+               type = GL_UNSIGNED_BYTE;
+               break;
+       case AV_PIX_FMT_RGB24:
+               internal_format = GL_RGBA8;
+               format = GL_RGB;
+               type = GL_UNSIGNED_BYTE;
+               break;
+       case AV_PIX_FMT_XYZ12:
+               internal_format = GL_RGBA12;
+               format = GL_RGB;
+               type = GL_UNSIGNED_SHORT;
+               break;
+       default:
+               throw PixelFormatError ("Texture::set", image->pixel_format());
+       }
+
+       bind ();
+
+       if (create) {
+               glTexImage2D (GL_TEXTURE_2D, 0, internal_format, _size->width, _size->height, 0, format, type, image->data()[0]);
+               check_gl_error ("glTexImage2D");
+       } else {
+               glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, _size->width, _size->height, format, type, image->data()[0]);
+               check_gl_error ("glTexSubImage2D");
+       }
+}
+
index 36edd6b8b979111285a7df2dcc4421efbd37c0f1..d7f8429af78c705faff83d831d1cd888be16fa42 100644 (file)
@@ -34,27 +34,51 @@ DCPOMATIC_ENABLE_WARNINGS
 #undef Success
 #undef Status
 
+
+class Texture
+{
+public:
+       Texture (GLint unpack_alignment);
+       ~Texture ();
+
+       Texture (Texture const&) = delete;
+       Texture& operator= (Texture const&) = delete;
+
+       void bind ();
+       void set (std::shared_ptr<const Image> image);
+
+private:
+       GLuint _name;
+       GLint _unpack_alignment;
+       boost::optional<dcp::Size> _size;
+};
+
+
 class GLVideoView : public VideoView
 {
 public:
        GLVideoView (FilmViewer* viewer, wxWindow* parent);
        ~GLVideoView ();
 
-       wxWindow* get () const {
+       wxWindow* get () const override {
                return _canvas;
        }
-       void update ();
-       void start ();
-       void stop ();
+       void update () override;
+       void start () override;
+       void stop () override;
 
-       NextFrameResult display_next_frame (bool);
+       NextFrameResult display_next_frame (bool) override;
 
        bool vsync_enabled () const {
                return _vsync_enabled;
        }
 
+       std::map<GLenum, std::string> information () const {
+               return _information;
+       }
+
 private:
-       void set_image (std::shared_ptr<const Image> image);
+       void set_image (std::shared_ptr<const PlayerVideo> pv);
        void set_image_and_draw ();
        void draw (Position<int> inter_position, dcp::Size inter_size);
        void thread ();
@@ -63,15 +87,43 @@ private:
        void check_for_butler_errors ();
        void ensure_context ();
        void size_changed (wxSizeEvent const &);
+       void setup_shaders ();
+       void set_border_colour (GLuint program);
 
        wxGLCanvas* _canvas;
        wxGLContext* _context;
 
-       boost::atomic<wxSize> _canvas_size;
+       template <class T>
+       class Last
+       {
+       public:
+               void set_next (T const& next) {
+                       _next = next;
+               }
+
+               bool changed () const {
+                       return !_value || *_value != _next;
+               }
+
+               void update () {
+                       _value = _next;
+               }
+
+       private:
+               boost::optional<T> _value;
+               T _next;
+       };
+
+       Last<wxSize> _last_canvas_size;
+       Last<dcp::Size> _last_video_size;
+       Last<Position<int>> _last_inter_position;
+       Last<dcp::Size> _last_inter_size;
+       Last<dcp::Size> _last_out_size;
 
-       GLuint _id;
-       boost::optional<dcp::Size> _size;
-       bool _have_storage;
+       boost::atomic<wxSize> _canvas_size;
+       std::unique_ptr<Texture> _video_texture;
+       std::unique_ptr<Texture> _subtitle_texture;
+       bool _have_subtitle_to_render = false;
        bool _vsync_enabled;
        boost::thread _thread;
 
@@ -80,5 +132,11 @@ private:
        boost::atomic<bool> _playing;
        boost::atomic<bool> _one_shot;
 
+       GLuint _vao;
+       GLint _fragment_type;
+       bool _setup_shaders_done = false;
+
        std::shared_ptr<wxTimer> _timer;
+
+       std::map<GLenum, std::string> _information;
 };
index d65cb0fccaf2999574405a86c73017823916cba3..129e0ceca407eee9990cac7a69e092552c8bb90e 100644 (file)
@@ -109,7 +109,7 @@ PlaylistControls::PlaylistControls (wxWindow* parent, shared_ptr<FilmViewer> vie
        _previous_button->Bind (wxEVT_BUTTON, boost::bind(&PlaylistControls::previous_clicked,  this));
        _spl_view->Bind        (wxEVT_LIST_ITEM_SELECTED,   boost::bind(&PlaylistControls::spl_selection_changed, this));
        _spl_view->Bind        (wxEVT_LIST_ITEM_DESELECTED, boost::bind(&PlaylistControls::spl_selection_changed, this));
-       _viewer->Finished.connect (boost::bind(&PlaylistControls::viewer_finished, this));
+       viewer->Finished.connect (boost::bind(&PlaylistControls::viewer_finished, this));
        _refresh_spl_view->Bind (wxEVT_BUTTON, boost::bind(&PlaylistControls::update_playlist_directory, this));
        _refresh_content_view->Bind (wxEVT_BUTTON, boost::bind(&ContentView::update, _content_view));
 
@@ -148,18 +148,26 @@ PlaylistControls::deselect_playlist ()
 void
 PlaylistControls::play_clicked ()
 {
-       _viewer->start ();
+       auto viewer = _viewer.lock ();
+       if (viewer) {
+               viewer->start ();
+       }
 }
 
 void
 PlaylistControls::setup_sensitivity ()
 {
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
        Controls::setup_sensitivity ();
        bool const active_job = _active_job && *_active_job != "examine_content";
        bool const c = _film && !_film->content().empty() && !active_job;
-       _play_button->Enable (c && !_viewer->playing());
-       _pause_button->Enable (_viewer->playing());
-       _spl_view->Enable (!_viewer->playing());
+       _play_button->Enable (c && !viewer->playing());
+       _pause_button->Enable (viewer->playing());
+       _spl_view->Enable (!viewer->playing());
        _next_button->Enable (can_do_next());
        _previous_button->Enable (can_do_previous());
 }
@@ -167,14 +175,22 @@ PlaylistControls::setup_sensitivity ()
 void
 PlaylistControls::pause_clicked ()
 {
-       _viewer->stop ();
+       auto viewer = _viewer.lock ();
+       if (viewer) {
+               viewer->stop ();
+       }
 }
 
 void
 PlaylistControls::stop_clicked ()
 {
-       _viewer->stop ();
-       _viewer->seek (DCPTime(), true);
+       auto viewer = _viewer.lock ();
+       if (!viewer) {
+               return;
+       }
+
+       viewer->stop ();
+       viewer->seek (DCPTime(), true);
        if (_selected_playlist) {
                _selected_playlist_position = 0;
                update_current_content ();
@@ -436,7 +452,8 @@ PlaylistControls::update_current_content ()
 void
 PlaylistControls::viewer_finished ()
 {
-       if (!_selected_playlist) {
+       auto viewer = _viewer.lock ();
+       if (!_selected_playlist || !viewer) {
                return;
        }
 
@@ -444,7 +461,7 @@ PlaylistControls::viewer_finished ()
        if (_selected_playlist_position < int(_playlists[*_selected_playlist].get().size())) {
                /* Next piece of content on the SPL */
                update_current_content ();
-               _viewer->start ();
+               viewer->start ();
        } else {
                /* Finished the whole SPL */
                _selected_playlist_position = 0;
index f5499ad9d4f6b93aa27113f7ed8d19992acd1abe..1ac56bbfe1f0ca3af029b6840511124af242a630 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2019-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
-#include "simple_video_view.h"
+
+#include "closed_captions_dialog.h"
 #include "film_viewer.h"
+#include "simple_video_view.h"
 #include "wx_util.h"
-#include "closed_captions_dialog.h"
-#include "lib/image.h"
-#include "lib/dcpomatic_log.h"
 #include "lib/butler.h"
+#include "lib/dcpomatic_log.h"
+#include "lib/image.h"
 #include <dcp/util.h>
 #include <wx/wx.h>
 #include <boost/bind/bind.hpp>
 
+
 using std::max;
+using std::shared_ptr;
 using std::string;
 using boost::optional;
-using std::shared_ptr;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
 #endif
@@ -57,18 +59,21 @@ SimpleVideoView::SimpleVideoView (FilmViewer* viewer, wxWindow* parent)
        _timer.Bind (wxEVT_TIMER, boost::bind(&SimpleVideoView::timer, this));
 }
 
+
 void
 SimpleVideoView::paint ()
 {
         _state_timer.set("paint-panel");
        wxPaintDC dc (_panel);
 
-       dcp::Size const out_size = _viewer->out_size ();
-       wxSize const panel_size = _panel->GetSize ();
+       auto const panel_size = _panel->GetSize ();
 
-       if (!out_size.width || !out_size.height || !_image || out_size != _image->size()) {
+       dcp::Size out_size;
+       if (!_image) {
                dc.Clear ();
        } else {
+               DCPOMATIC_ASSERT (_image->alignment() == Image::Alignment::COMPACT);
+               out_size = _image->size();
                wxImage frame (out_size.width, out_size.height, _image->data()[0], true);
                wxBitmap frame_bitmap (frame);
                dc.DrawBitmap (frame_bitmap, 0, max(0, (panel_size.GetHeight() - out_size.height) / 2));
@@ -112,6 +117,7 @@ SimpleVideoView::paint ()
         _state_timer.unset();
 }
 
+
 void
 SimpleVideoView::refresh_panel ()
 {
@@ -121,6 +127,7 @@ SimpleVideoView::refresh_panel ()
        _state_timer.unset ();
 }
 
+
 void
 SimpleVideoView::timer ()
 {
@@ -144,6 +151,7 @@ SimpleVideoView::timer ()
        }
 }
 
+
 void
 SimpleVideoView::start ()
 {
@@ -151,6 +159,7 @@ SimpleVideoView::start ()
        timer ();
 }
 
+
 /** Try to get a frame from the butler and display it.
  *  @param non_blocking true to return false quickly if no video is available quickly (i.e. we are waiting for the butler).
  *  false to ask the butler to block until it has video (unless it is suspended).
@@ -175,11 +184,12 @@ SimpleVideoView::display_next_frame (bool non_blocking)
        return SUCCESS;
 }
 
+
 void
 SimpleVideoView::update ()
 {
        if (!player_video().first) {
-               set_image (shared_ptr<Image>());
+               _image.reset ();
                refresh_panel ();
                return;
        }
@@ -212,9 +222,7 @@ SimpleVideoView::update ()
 
        _state_timer.set ("get image");
 
-       set_image (
-               player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true)
-               );
+       _image = player_video().first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, true);
 
        _state_timer.set ("ImageChanged");
        _viewer->image_changed (player_video().first);
index 31756b5d85eceecbe62a693a5ed524adeb6edf5f..cbb162023c29e402bb0561b0570a62cc0b8a38b7 100644 (file)
@@ -33,19 +33,15 @@ class SimpleVideoView : public VideoView
 public:
        SimpleVideoView (FilmViewer* viewer, wxWindow* parent);
 
-       wxWindow* get () const {
+       wxWindow* get () const override {
                return _panel;
        }
 
-       void update ();
-       void start ();
-       NextFrameResult display_next_frame (bool non_blocking);
+       void update () override;
+       void start () override;
+       NextFrameResult display_next_frame (bool non_blocking) override;
 
 private:
-       void set_image (std::shared_ptr<const Image> image) {
-               _image = image;
-       }
-
        void refresh_panel ();
        void paint ();
        void timer ();
index 1e4ecc8d7fc74e5469c76759272110d209f42f1d..6196c1b5c29e10c39fcb55277a7e833116d7cf3c 100644 (file)
@@ -63,14 +63,15 @@ StandardControls::play_clicked ()
 void
 StandardControls::check_play_state ()
 {
-       if (!_film || _film->video_frame_rate() == 0) {
+       auto viewer = _viewer.lock ();
+       if (!_film || _film->video_frame_rate() == 0 || !viewer) {
                return;
        }
 
        if (_play_button->GetValue()) {
-               _viewer->start ();
+               viewer->start ();
        } else {
-               _viewer->stop ();
+               viewer->stop ();
        }
 }
 
index 968cd57404dd729957d97f842a7dd82482771807..1c8dd8d00c2757defff1c1772739bd32aa4c1aac 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2019-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
+#include "film_viewer.h"
+#include "gl_video_view.h"
 #include "system_information_dialog.h"
 #include "wx_util.h"
-#include "gl_video_view.h"
-#include "film_viewer.h"
+
 
 #ifdef DCPOMATIC_OSX
 #include <OpenGL/glu.h>
 #include <GL/glext.h>
 #endif
 
+
 using std::string;
 using std::weak_ptr;
 using std::shared_ptr;
 
+
 SystemInformationDialog::SystemInformationDialog (wxWindow* parent, weak_ptr<FilmViewer> weak_viewer)
        : TableDialog (parent, _("System information"), 2, 1, false)
 {
-       shared_ptr<FilmViewer> viewer = weak_viewer.lock ();
-       GLVideoView const * gl = viewer ? dynamic_cast<GLVideoView const *>(viewer->video_view()) : 0;
+       auto viewer = weak_viewer.lock ();
+       shared_ptr<const GLVideoView> gl;
+       if (viewer) {
+               gl = std::dynamic_pointer_cast<const GLVideoView>(viewer->video_view());
+       }
 
        if (!gl) {
                add (_("OpenGL version"), true);
                add (_("unknown (OpenGL not enabled in DCP-o-matic)"), false);
        } else {
-               add (_("OpenGL version"), true);
-               char const * v = (char const *) glGetString (GL_VERSION);
-               if (v) {
-                       add (std_to_wx(v), false);
-               } else {
-                       add (_("unknown"), false);
-               }
+
+               auto information = gl->information();
+               auto add_string = [this, &information](GLenum name, wxString label) {
+                       add (label, true);
+                       auto i = information.find(name);
+                       if (i != information.end()) {
+                               add (std_to_wx(i->second), false);
+                       } else {
+                               add (_("unknown"), false);
+                       }
+               };
+
+               add_string (GL_VENDOR, _("Vendor"));
+               add_string (GL_RENDERER, _("Renderer"));
+               add_string (GL_VERSION, _("Version"));
+               add_string (GL_SHADING_LANGUAGE_VERSION, _("Shading language version"));
+
                add (_("vsync"), true);
                add (gl->vsync_enabled() ? _("enabled") : _("not enabled"), false);
        }
index 5b9efa2347a449e37c92e1edc1ae0fad61ff4221..49e617a6eb2b89a816125704ab129a9317fd9db2 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2019-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -25,9 +25,9 @@
 
 class FilmViewer;
 
+
 class SystemInformationDialog : public TableDialog
 {
 public:
        SystemInformationDialog (wxWindow* parent, std::weak_ptr<FilmViewer> viewer);
-
 };
index 4d80e2535248ceaccc35621bfa6c59435c3768b8..1d10beb30b7bd74255ea2e63153c311e12b11bef 100644 (file)
@@ -28,7 +28,6 @@
 #include <sys/time.h>
 
 
-using std::pair;
 using std::shared_ptr;
 using boost::optional;
 
index 9517a3bf479a2f31b8d9c7fdfbff955fa29e9509..5353f213f8803e3e2a9865c6a0567610edf51fc9 100644 (file)
@@ -127,6 +127,10 @@ public:
                _three_d = t;
        }
 
+       void set_optimise_for_j2k (bool o) {
+               _optimise_for_j2k = o;
+       }
+
 protected:
        NextFrameResult get_next_frame (bool non_blocking);
        boost::optional<int> time_until_next_frame () const;
@@ -168,6 +172,8 @@ protected:
 
        StateTimer _state_timer;
 
+       bool _optimise_for_j2k = false;
+
 private:
        /** Mutex protecting all the state in this class */
        mutable boost::mutex _mutex;
index 2e45f3493b69e1e0af0f29f95dfb2c9dbe538c40..07b2955b37d6d4da579ee227b1fa8bd5178b3aa1 100644 (file)
@@ -155,7 +155,7 @@ VideoWaveformPlot::create_waveform ()
 
        auto const image_size = _image->size();
        int const waveform_height = GetSize().GetHeight() - _vertical_margin * 2;
-       _waveform = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size (image_size.width, waveform_height), true);
+       _waveform = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size (image_size.width, waveform_height), Image::Alignment::PADDED);
 
        for (int x = 0; x < image_size.width; ++x) {
 
@@ -182,7 +182,7 @@ VideoWaveformPlot::create_waveform ()
 
        _waveform = _waveform->scale (
                dcp::Size (GetSize().GetWidth() - _x_axis_width, waveform_height),
-               dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, false, false
+               dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, Image::Alignment::COMPACT, false
                );
 }
 
index b868292f583e6db60fa20eb22ed2d1c05620b433..50c078f2b0f1d50a108d8707727215c19bc8d4a9 100644 (file)
@@ -209,7 +209,7 @@ def configure(conf):
                                mandatory=True)
 
     if conf.env.TARGET_LINUX:
-        conf.env.append_value('CXXFLAGS', ['-DGLX_GLXEXT_PROTOTYPES'])
+        conf.env.append_value('CXXFLAGS', ['-DGL_GLEXT_PROTOTYPES', '-DGLX_GLXEXT_PROTOTYPES'])
     if conf.env.TARGET_WINDOWS:
         conf.env.append_value('CXXFLAGS', ['-DWGL_WGLEXT_PROTOTYPES'])
 
@@ -277,6 +277,8 @@ def configure(conf):
     if conf.env.TARGET_WINDOWS or conf.env.TARGET_LINUX:
         conf.check_cfg(package='gl', args='--cflags --libs', uselib_store='GL', mandatory=True)
         conf.check_cfg(package='glu', args='--cflags --libs', uselib_store='GLU', mandatory=True)
+        if conf.env.TARGET_WINDOWS:
+            conf.check_cfg(package='glew', args='--cflags --libs', uselib_store='GLEW', mandatory=True)
     else:
         conf.env.STLIB_GL = 'gl'
         conf.env.STLIB_GLU = 'glu'
@@ -311,7 +313,7 @@ def build(bld):
     if bld.env.TARGET_LINUX:
         obj.uselib += 'GTK GL GLU '
     if bld.env.TARGET_WINDOWS:
-        obj.uselib += 'WINSOCK2 OLE32 DSOUND WINMM KSUSER GL GLU '
+        obj.uselib += 'WINSOCK2 OLE32 DSOUND WINMM KSUSER GL GLU GLEW '
     if bld.env.TARGET_OSX:
         obj.framework = ['CoreAudio', 'OpenGL']
     obj.use = 'libdcpomatic2'
index dbd7a287edd1308e9047b66d9b3047bba562a81c..787d1c3248a695e9f8ad114124280b72cd7aaa0f 100644 (file)
@@ -59,7 +59,7 @@ BOOST_AUTO_TEST_CASE (butler_test1)
                map.set (i, i, 1);
        }
 
-       Butler butler (film, make_shared<Player>(film), map, 6, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, false);
+       Butler butler (film, make_shared<Player>(film, Image::Alignment::COMPACT), map, 6, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, Image::Alignment::COMPACT, false, false);
 
        BOOST_CHECK (butler.get_video(true, 0).second == DCPTime());
        BOOST_CHECK (butler.get_video(true, 0).second == DCPTime::from_frames(1, 24));
index f518f938358ebd39881f77c9e8263eac5d9834a5..7a99f72274758a1bace8f1e9f637fe7e1c61d502 100644 (file)
@@ -66,7 +66,7 @@ do_remote_encode (shared_ptr<DCPVideo> frame, EncodeServerDescription descriptio
 
 BOOST_AUTO_TEST_CASE (client_server_test_rgb)
 {
-       auto image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size (1998, 1080), true);
+       auto image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size (1998, 1080), Image::Alignment::PADDED);
        uint8_t* p = image->data()[0];
 
        for (int y = 0; y < 1080; ++y) {
@@ -79,7 +79,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb)
                p += image->stride()[0];
        }
 
-       auto sub_image = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (100, 200), true);
+       auto sub_image = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (100, 200), Image::Alignment::PADDED);
        p = sub_image->data()[0];
        for (int y = 0; y < 200; ++y) {
                uint8_t* q = p;
@@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb)
 
 BOOST_AUTO_TEST_CASE (client_server_test_yuv)
 {
-       auto image = make_shared<Image>(AV_PIX_FMT_YUV420P, dcp::Size (1998, 1080), true);
+       auto image = make_shared<Image>(AV_PIX_FMT_YUV420P, dcp::Size (1998, 1080), Image::Alignment::PADDED);
 
        for (int i = 0; i < image->planes(); ++i) {
                uint8_t* p = image->data()[i];
@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv)
                }
        }
 
-       auto sub_image = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (100, 200), true);
+       auto sub_image = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (100, 200), Image::Alignment::PADDED);
        uint8_t* p = sub_image->data()[0];
        for (int y = 0; y < 200; ++y) {
                uint8_t* q = p;
@@ -236,7 +236,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv)
 
 BOOST_AUTO_TEST_CASE (client_server_test_j2k)
 {
-       auto image = make_shared<Image>(AV_PIX_FMT_YUV420P, dcp::Size (1998, 1080), true);
+       auto image = make_shared<Image>(AV_PIX_FMT_YUV420P, dcp::Size (1998, 1080), Image::Alignment::PADDED);
 
        for (int i = 0; i < image->planes(); ++i) {
                uint8_t* p = image->data()[i];
index a52a0ccc59941fb0af7c687bf7f45682d7652747..66cd402c487e415d1a167532048621b40ab3e8e3 100644 (file)
@@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE (check_reuse_old_data_test)
        ov_content = make_shared<DCPContent>(ov->dir(ov->dcp_name(false)));
        test->examine_and_add_content (ov_content);
        BOOST_REQUIRE (!wait_for_jobs());
-       auto player = make_shared<Player>(test);
+       auto player = make_shared<Player>(test, Image::Alignment::COMPACT);
 
        auto decoder = std::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
        BOOST_REQUIRE (decoder);
@@ -105,7 +105,7 @@ BOOST_AUTO_TEST_CASE (check_reuse_old_data_test)
        auto vf_content = make_shared<DCPContent>(vf->dir(vf->dcp_name(false)));
        test->examine_and_add_content (vf_content);
        BOOST_REQUIRE (!wait_for_jobs());
-       player.reset (new Player(test));
+       player = make_shared<Player>(test, Image::Alignment::COMPACT);
 
        decoder = std::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
        BOOST_REQUIRE (decoder);
@@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE (check_reuse_old_data_test)
        auto encrypted_content = make_shared<DCPContent>(encrypted->dir(encrypted->dcp_name(false)));
        test->examine_and_add_content (encrypted_content);
        BOOST_REQUIRE (!wait_for_jobs());
-       player = make_shared<Player>(test);
+       player = make_shared<Player>(test, Image::Alignment::COMPACT);
 
        decoder = std::dynamic_pointer_cast<DCPDecoder>(player->_pieces.front()->decoder);
        BOOST_REQUIRE (decoder);
index ef1b623e78ffc8f7a67ef1f46dbd9ae37628ecb9..2ab7eaec37deb90d6b0ad1ea3ca6f649ad5ae251 100644 (file)
@@ -28,9 +28,7 @@
 
 
 using std::make_shared;
-using std::pair;
-using std::shared_ptr;
-using boost::optional;
+using std::make_shared;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
 #endif
@@ -47,13 +45,14 @@ BOOST_AUTO_TEST_CASE (dcp_playback_test)
 
        auto butler = std::make_shared<Butler>(
                film,
-               shared_ptr<Player>(new Player(film)),
+               make_shared<Player>(film, Image::Alignment::PADDED),
                AudioMapping(6, 6),
                6,
                bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24),
                VideoRange::FULL,
-               false,
-               true
+               Image::Alignment::PADDED,
+               true,
+               false
                );
 
        auto audio_buffer = new float[2000 * 6];
@@ -64,7 +63,7 @@ BOOST_AUTO_TEST_CASE (dcp_playback_test)
                }
                /* assuming DCP is 24fps/48kHz */
                butler->get_audio (audio_buffer, 2000);
-               p.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true);
+               p.first->image(bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, true);
        }
        delete[] audio_buffer;
 }
index a68f7cf5761859447e6a10790305608941eb8532..40c909b394b549d89d0694f0a2e0878c5a55a743 100644 (file)
@@ -101,7 +101,7 @@ test (boost::filesystem::path file)
        ref_buffer_size = info.samplerate * info.channels;
        ref_buffer = new float[ref_buffer_size];
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
 
        player->Audio.connect (bind (&audio, _1, info.channels));
        while (!player->pass ()) {}
index 5a36b99f40c23600ea98ef637e27a6f83538b9a8..6bdadce97e1027b6211c042fbd0a9571bd2ed7a3 100644 (file)
@@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_audio_test2)
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs ());
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
        while (!player->pass ()) {}
 }
 
@@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_audio_test3)
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs ());
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
        player->set_fast ();
        while (!player->pass ()) {}
 }
@@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_audio_test4)
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs ());
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
        player->set_fast ();
        BOOST_CHECK_NO_THROW (while (!player->pass()) {});
 }
index b2069a8b16b522bd5163a45ebe16d30782997b8d..73eea719ff7e7ba37cb027278618d220acfbaf79 100644 (file)
@@ -75,7 +75,7 @@ ffmpeg_decoder_sequential_test_one (boost::filesystem::path file, float fps, int
        film->examine_and_add_content (content);
        BOOST_REQUIRE (!wait_for_jobs());
        film->write_metadata ();
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
 
        BOOST_REQUIRE (content->video_frame_rate());
        BOOST_CHECK_CLOSE (content->video_frame_rate().get(), fps, 0.01);
index 3993b3efbdeaf7f99e4cff9f4b1f04caf4d7c4dd..7369bd24feac9ec03e4a21d2fb86aaa8bef7389d 100644 (file)
 using std::cout;
 using std::list;
 using std::make_shared;
-using std::shared_ptr;
 using std::string;
 
 
 BOOST_AUTO_TEST_CASE (aligned_image_test)
 {
-       auto s = new Image (AV_PIX_FMT_RGB24, dcp::Size (50, 50), true);
+       auto s = new Image (AV_PIX_FMT_RGB24, dcp::Size (50, 50), Image::Alignment::PADDED);
        BOOST_CHECK_EQUAL (s->planes(), 1);
        /* 192 is 150 aligned to the nearest 64 bytes */
        BOOST_CHECK_EQUAL (s->stride()[0], 192);
@@ -72,7 +71,7 @@ BOOST_AUTO_TEST_CASE (aligned_image_test)
        BOOST_CHECK_EQUAL (t->stride()[0], s->stride()[0]);
 
        /* assignment operator */
-       auto u = new Image (AV_PIX_FMT_YUV422P, dcp::Size (150, 150), false);
+       auto u = new Image (AV_PIX_FMT_YUV422P, dcp::Size (150, 150), Image::Alignment::COMPACT);
        *u = *s;
        BOOST_CHECK_EQUAL (u->planes(), 1);
        BOOST_CHECK_EQUAL (u->stride()[0], 192);
@@ -96,7 +95,7 @@ BOOST_AUTO_TEST_CASE (aligned_image_test)
 
 BOOST_AUTO_TEST_CASE (compact_image_test)
 {
-       auto s = new Image (AV_PIX_FMT_RGB24, dcp::Size (50, 50), false);
+       auto s = new Image (AV_PIX_FMT_RGB24, dcp::Size (50, 50), Image::Alignment::COMPACT);
        BOOST_CHECK_EQUAL (s->planes(), 1);
        BOOST_CHECK_EQUAL (s->stride()[0], 50 * 3);
        BOOST_CHECK_EQUAL (s->line_size()[0], 50 * 3);
@@ -122,7 +121,7 @@ BOOST_AUTO_TEST_CASE (compact_image_test)
        BOOST_CHECK_EQUAL (t->stride()[0], s->stride()[0]);
 
        /* assignment operator */
-       auto u = new Image (AV_PIX_FMT_YUV422P, dcp::Size (150, 150), true);
+       auto u = new Image (AV_PIX_FMT_YUV422P, dcp::Size (150, 150), Image::Alignment::PADDED);
        *u = *s;
        BOOST_CHECK_EQUAL (u->planes(), 1);
        BOOST_CHECK_EQUAL (u->stride()[0], 50 * 3);
@@ -148,10 +147,10 @@ void
 alpha_blend_test_one (AVPixelFormat format, string suffix)
 {
        auto proxy = make_shared<FFmpegImageProxy>(TestPaths::private_data() / "prophet_frame.tiff");
-       auto raw = proxy->image().image;
-       auto background = raw->convert_pixel_format (dcp::YUVToRGB::REC709, format, true, false);
+       auto raw = proxy->image(Image::Alignment::PADDED).image;
+       auto background = raw->convert_pixel_format (dcp::YUVToRGB::REC709, format, Image::Alignment::PADDED, false);
 
-       auto overlay = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size(431, 891), true);
+       auto overlay = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size(431, 891), Image::Alignment::PADDED);
        overlay->make_transparent ();
 
        for (int y = 0; y < 128; ++y) {
@@ -180,7 +179,7 @@ alpha_blend_test_one (AVPixelFormat format, string suffix)
 
        background->alpha_blend (overlay, Position<int> (13, 17));
 
-       auto save = background->convert_pixel_format (dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, false, false);
+       auto save = background->convert_pixel_format (dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, Image::Alignment::COMPACT, false);
 
        write_image (save, "build/test/image_test_" + suffix + ".png");
        check_image ("build/test/image_test_" + suffix + ".png", TestPaths::private_data() / ("image_test_" + suffix + ".png"));
@@ -205,7 +204,7 @@ BOOST_AUTO_TEST_CASE (merge_test1)
 {
        int const stride = 48 * 4;
 
-       auto A = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (48, 48), false);
+       auto A = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (48, 48), Image::Alignment::COMPACT);
        A->make_transparent ();
        auto a = A->data()[0];
 
@@ -221,7 +220,7 @@ BOOST_AUTO_TEST_CASE (merge_test1)
 
        list<PositionImage> all;
        all.push_back (PositionImage (A, Position<int>(0, 0)));
-       auto merged = merge (all);
+       auto merged = merge (all, Image::Alignment::COMPACT);
 
        BOOST_CHECK (merged.position == Position<int>(0, 0));
        BOOST_CHECK_EQUAL (memcmp (merged.image->data()[0], A->data()[0], stride * 48), 0);
@@ -231,7 +230,7 @@ BOOST_AUTO_TEST_CASE (merge_test1)
 /** Test merge (list<PositionImage>) with two images */
 BOOST_AUTO_TEST_CASE (merge_test2)
 {
-       auto A = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (48, 1), false);
+       auto A = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (48, 1), Image::Alignment::COMPACT);
        A->make_transparent ();
        auto a = A->data()[0];
        for (int x = 0; x < 16; ++x) {
@@ -241,7 +240,7 @@ BOOST_AUTO_TEST_CASE (merge_test2)
                a[x * 4 + 3] = 255;
        }
 
-       auto B = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (48, 1), false);
+       auto B = make_shared<Image>(AV_PIX_FMT_BGRA, dcp::Size (48, 1), Image::Alignment::COMPACT);
        B->make_transparent ();
        auto b = B->data()[0];
        for (int x = 0; x < 16; ++x) {
@@ -254,7 +253,7 @@ BOOST_AUTO_TEST_CASE (merge_test2)
        list<PositionImage> all;
        all.push_back (PositionImage(A, Position<int>(0, 0)));
        all.push_back (PositionImage(B, Position<int>(0, 0)));
-       auto merged = merge (all);
+       auto merged = merge (all, Image::Alignment::COMPACT);
 
        BOOST_CHECK (merged.position == Position<int>(0, 0));
 
@@ -274,11 +273,11 @@ BOOST_AUTO_TEST_CASE (merge_test2)
 BOOST_AUTO_TEST_CASE (crop_scale_window_test)
 {
        auto proxy = make_shared<FFmpegImageProxy>("test/data/flat_red.png");
-       auto raw = proxy->image().image;
+       auto raw = proxy->image(Image::Alignment::PADDED).image;
        auto out = raw->crop_scale_window(
-               Crop(), dcp::Size(1998, 836), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_YUV420P, VideoRange::FULL, true, false
+               Crop(), dcp::Size(1998, 836), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_YUV420P, VideoRange::FULL, Image::Alignment::PADDED, false
                );
-       auto save = out->scale(dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, false, false);
+       auto save = out->scale(dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, Image::Alignment::COMPACT, false);
        write_image(save, "build/test/crop_scale_window_test.png");
        check_image("test/data/crop_scale_window_test.png", "build/test/crop_scale_window_test.png");
 }
@@ -287,12 +286,12 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test)
 /** Special cases of Image::crop_scale_window which triggered some valgrind warnings */
 BOOST_AUTO_TEST_CASE (crop_scale_window_test2)
 {
-       auto image = make_shared<Image>(AV_PIX_FMT_XYZ12LE, dcp::Size(2048, 858), true);
+       auto image = make_shared<Image>(AV_PIX_FMT_XYZ12LE, dcp::Size(2048, 858), Image::Alignment::PADDED);
        image->crop_scale_window (
-               Crop(279, 0, 0, 0), dcp::Size(1069, 448), dcp::Size(1069, 578), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, false, false
+               Crop(279, 0, 0, 0), dcp::Size(1069, 448), dcp::Size(1069, 578), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, Image::Alignment::COMPACT, false
                );
        image->crop_scale_window (
-               Crop(2048, 0, 0, 0), dcp::Size(1069, 448), dcp::Size(1069, 578), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, false, false
+               Crop(2048, 0, 0, 0), dcp::Size(1069, 448), dcp::Size(1069, 578), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, Image::Alignment::COMPACT, false
                );
 }
 
@@ -300,9 +299,9 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test2)
 BOOST_AUTO_TEST_CASE (crop_scale_window_test3)
 {
        auto proxy = make_shared<FFmpegImageProxy>(TestPaths::private_data() / "player_seek_test_0.png");
-       auto xyz = proxy->image().image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, true, false);
+       auto xyz = proxy->image(Image::Alignment::PADDED).image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, Image::Alignment::PADDED, false);
        auto cropped = xyz->crop_scale_window(
-               Crop(512, 0, 0, 0), dcp::Size(1486, 1080), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, false, false
+               Crop(512, 0, 0, 0), dcp::Size(1486, 1080), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, Image::Alignment::COMPACT, false
                );
        write_image(cropped, "build/test/crop_scale_window_test3.png");
        check_image("test/data/crop_scale_window_test3.png", "build/test/crop_scale_window_test3.png");
@@ -312,9 +311,9 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test3)
 BOOST_AUTO_TEST_CASE (crop_scale_window_test4)
 {
        auto proxy = make_shared<FFmpegImageProxy>(TestPaths::private_data() / "player_seek_test_0.png");
-       auto xyz = proxy->image().image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, true, false);
+       auto xyz = proxy->image(Image::Alignment::PADDED).image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGB24, Image::Alignment::PADDED, false);
        auto cropped = xyz->crop_scale_window(
-               Crop(512, 0, 0, 0), dcp::Size(1486, 1080), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_XYZ12LE, VideoRange::FULL, false, false
+               Crop(512, 0, 0, 0), dcp::Size(1486, 1080), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_XYZ12LE, VideoRange::FULL, Image::Alignment::COMPACT, false
                );
        write_image(cropped, "build/test/crop_scale_window_test4.png");
        check_image("test/data/crop_scale_window_test4.png", "build/test/crop_scale_window_test4.png", 35000);
@@ -324,9 +323,9 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test4)
 BOOST_AUTO_TEST_CASE (crop_scale_window_test5)
 {
        auto proxy = make_shared<FFmpegImageProxy>(TestPaths::private_data() / "player_seek_test_0.png");
-       auto xyz = proxy->image().image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_XYZ12LE, true, false);
+       auto xyz = proxy->image(Image::Alignment::PADDED).image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_XYZ12LE, Image::Alignment::PADDED, false);
        auto cropped = xyz->crop_scale_window(
-               Crop(512, 0, 0, 0), dcp::Size(1486, 1080), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, false, false
+               Crop(512, 0, 0, 0), dcp::Size(1486, 1080), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, Image::Alignment::COMPACT, false
                );
        write_image(cropped, "build/test/crop_scale_window_test5.png");
        check_image("test/data/crop_scale_window_test5.png", "build/test/crop_scale_window_test5.png");
@@ -336,9 +335,9 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test5)
 BOOST_AUTO_TEST_CASE (crop_scale_window_test6)
 {
        auto proxy = make_shared<FFmpegImageProxy>(TestPaths::private_data() / "player_seek_test_0.png");
-       auto xyz = proxy->image().image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_XYZ12LE, true, false);
+       auto xyz = proxy->image(Image::Alignment::PADDED).image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_XYZ12LE, Image::Alignment::PADDED, false);
        auto cropped = xyz->crop_scale_window(
-               Crop(512, 0, 0, 0), dcp::Size(1486, 1080), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_XYZ12LE, VideoRange::FULL, false, false
+               Crop(512, 0, 0, 0), dcp::Size(1486, 1080), dcp::Size(1998, 1080), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_XYZ12LE, VideoRange::FULL, Image::Alignment::COMPACT, false
                );
        write_image(cropped, "build/test/crop_scale_window_test6.png");
        check_image("test/data/crop_scale_window_test6.png", "build/test/crop_scale_window_test6.png", 35000);
@@ -351,7 +350,7 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test7)
        using namespace boost::filesystem;
        for (int left_crop = 0; left_crop < 8; ++left_crop) {
                auto proxy = make_shared<FFmpegImageProxy>("test/data/rgb_grey_testcard.png");
-               auto yuv = proxy->image().image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_YUV420P, true, false);
+               auto yuv = proxy->image(Image::Alignment::PADDED).image->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_YUV420P, Image::Alignment::PADDED, false);
                int rounded = left_crop - (left_crop % 2);
                auto cropped = yuv->crop_scale_window(
                        Crop(left_crop, 0, 0, 0),
@@ -361,7 +360,7 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test7)
                        VideoRange::VIDEO,
                        AV_PIX_FMT_RGB24,
                        VideoRange::VIDEO,
-                       true,
+                       Image::Alignment::PADDED,
                        false
                        );
                path file = String::compose("crop_scale_window_test7-%1.png", left_crop);
@@ -374,8 +373,8 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test7)
 BOOST_AUTO_TEST_CASE (as_png_test)
 {
        auto proxy = make_shared<FFmpegImageProxy>("test/data/3d_test/000001.png");
-       auto image_rgb = proxy->image().image;
-       auto image_bgr = image_rgb->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_BGRA, true, false);
+       auto image_rgb = proxy->image(Image::Alignment::PADDED).image;
+       auto image_bgr = image_rgb->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_BGRA, Image::Alignment::PADDED, false);
        image_rgb->as_png().write ("build/test/as_png_rgb.png");
        image_bgr->as_png().write ("build/test/as_png_bgr.png");
 
@@ -388,11 +387,11 @@ BOOST_AUTO_TEST_CASE (as_png_test)
 static void
 fade_test_format_black (AVPixelFormat f, string name)
 {
-       Image yuv (f, dcp::Size(640, 480), true);
+       Image yuv (f, dcp::Size(640, 480), Image::Alignment::PADDED);
        yuv.make_black ();
        yuv.fade (0);
        string const filename = "fade_test_black_" + name + ".png";
-       yuv.convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, true, false)->as_png().write("build/test/" + filename);
+       yuv.convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, Image::Alignment::PADDED, false)->as_png().write("build/test/" + filename);
        check_image ("test/data/" + filename, "build/test/" + filename);
 }
 
@@ -402,10 +401,10 @@ static void
 fade_test_format_red (AVPixelFormat f, float amount, string name)
 {
        auto proxy = make_shared<FFmpegImageProxy>("test/data/flat_red.png");
-       auto red = proxy->image().image->convert_pixel_format(dcp::YUVToRGB::REC709, f, true, false);
+       auto red = proxy->image(Image::Alignment::PADDED).image->convert_pixel_format(dcp::YUVToRGB::REC709, f, Image::Alignment::PADDED, false);
        red->fade (amount);
        string const filename = "fade_test_red_" + name + ".png";
-       red->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, true, false)->as_png().write("build/test/" + filename);
+       red->convert_pixel_format(dcp::YUVToRGB::REC709, AV_PIX_FMT_RGBA, Image::Alignment::PADDED, false)->as_png().write("build/test/" + filename);
        check_image ("test/data/" + filename, "build/test/" + filename);
 }
 
@@ -482,9 +481,9 @@ BOOST_AUTO_TEST_CASE (make_black_test)
 
        int N = 0;
        for (auto i: pix_fmts) {
-               auto foo = make_shared<Image>(i, in_size, true);
+               auto foo = make_shared<Image>(i, in_size, Image::Alignment::PADDED);
                foo->make_black ();
-               auto bar = foo->scale (out_size, dcp::YUVToRGB::REC601, AV_PIX_FMT_RGB24, true, false);
+               auto bar = foo->scale (out_size, dcp::YUVToRGB::REC601, AV_PIX_FMT_RGB24, Image::Alignment::PADDED, false);
 
                uint8_t* p = bar->data()[0];
                for (int y = 0; y < bar->size().height; ++y) {
@@ -506,7 +505,7 @@ BOOST_AUTO_TEST_CASE (make_black_test)
 BOOST_AUTO_TEST_CASE (make_part_black_test)
 {
        auto proxy = make_shared<FFmpegImageProxy>("test/data/flat_red.png");
-       auto original = proxy->image().image;
+       auto original = proxy->image(Image::Alignment::PADDED).image;
 
        list<AVPixelFormat> pix_fmts = {
                AV_PIX_FMT_RGB24,
@@ -526,9 +525,9 @@ BOOST_AUTO_TEST_CASE (make_part_black_test)
        int N = 0;
        for (auto i: pix_fmts) {
                for (auto j: positions) {
-                       auto foo = original->convert_pixel_format(dcp::YUVToRGB::REC601, i, true, false);
+                       auto foo = original->convert_pixel_format(dcp::YUVToRGB::REC601, i, Image::Alignment::PADDED, false);
                        foo->make_part_black (j.first, j.second);
-                       auto bar = foo->convert_pixel_format (dcp::YUVToRGB::REC601, AV_PIX_FMT_RGB24, true, false);
+                       auto bar = foo->convert_pixel_format (dcp::YUVToRGB::REC601, AV_PIX_FMT_RGB24, Image::Alignment::PADDED, false);
 
                        auto p = bar->data()[0];
                        for (int y = 0; y < bar->size().height; ++y) {
@@ -569,10 +568,10 @@ BOOST_AUTO_TEST_CASE (make_part_black_test)
  */
 BOOST_AUTO_TEST_CASE (over_crop_test)
 {
-       auto image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size(128, 128), true);
+       auto image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size(128, 128), Image::Alignment::PADDED);
        image->make_black ();
        auto scaled = image->crop_scale_window (
-               Crop(0, 0, 128, 128), dcp::Size(1323, 565), dcp::Size(1349, 565), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, true, true
+               Crop(0, 0, 128, 128), dcp::Size(1323, 565), dcp::Size(1349, 565), dcp::YUVToRGB::REC709, VideoRange::FULL, AV_PIX_FMT_RGB24, VideoRange::FULL, Image::Alignment::PADDED, true
                );
        string const filename = "over_crop_test.png";
        write_image (scaled, "build/test/" + filename);
index 33ce2635da73dbed50b287328d615c7241e2746d..7050dd771acc6dd74dd8a57f0e30b8d177010470 100644 (file)
@@ -35,10 +35,26 @@ using std::make_shared;
 
 BOOST_AUTO_TEST_CASE (low_bitrate_test)
 {
-       auto image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size(1998, 1080), true);
+       auto image = make_shared<Image>(AV_PIX_FMT_RGB24, dcp::Size(1998, 1080), Image::Alignment::PADDED);
        image->make_black ();
+
        auto proxy = make_shared<RawImageProxy>(image);
-       auto frame = make_shared<PlayerVideo>(proxy, Crop(), boost::optional<double>(), dcp::Size(1998, 1080), dcp::Size(1998, 1080), Eyes::BOTH, Part::WHOLE, boost::optional<ColourConversion>(), VideoRange::FULL, std::weak_ptr<Content>(), boost::optional<Frame>(), false);
+
+       auto frame = make_shared<PlayerVideo>(
+               proxy,
+               Crop(),
+               boost::optional<double>(),
+               dcp::Size(1998, 1080),
+               dcp::Size(1998, 1080),
+               Eyes::BOTH,
+               Part::WHOLE,
+               boost::optional<ColourConversion>(),
+               VideoRange::FULL,
+               std::weak_ptr<Content>(),
+               boost::optional<Frame>(),
+               false
+               );
+
        auto dcp_video = make_shared<DCPVideo>(frame, 0, 24, 100000000, Resolution::TWO_K);
        auto j2k = dcp_video->encode_locally();
        BOOST_REQUIRE (j2k.size() >= 16536);
index 724f4e75ba1354359ec5422a72f3d1aa2ace17ae..3c969921d8045dc956cc2e25369e28d2f89e181b 100644 (file)
@@ -40,7 +40,6 @@
 
 using std::dynamic_pointer_cast;
 using std::make_shared;
-using std::shared_ptr;
 
 
 BOOST_AUTO_TEST_CASE (overlap_video_test1)
@@ -57,7 +56,7 @@ BOOST_AUTO_TEST_CASE (overlap_video_test1)
        B->video->set_length (24);
        B->set_position (film, dcpomatic::DCPTime::from_seconds(1));
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
        auto pieces = player->_pieces;
        BOOST_REQUIRE_EQUAL (pieces.size(), 2U);
        BOOST_CHECK_EQUAL (pieces.front()->content, A);
index f59c594e9705f07ab1c405ad2bfd4b10118bcd3b..12a95bd6939b11d751edc6dd8c4e565a0f05d3bf 100644 (file)
@@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE (pixel_formats_test)
                f->height = 480;
                f->format = static_cast<int> (i.format);
                av_frame_get_buffer (f, true);
-               Image t (f);
+               Image t (f, Image::Alignment::COMPACT);
                BOOST_CHECK_EQUAL(t.planes(), i.planes);
                BOOST_CHECK_EQUAL(t.sample_size(0).height, i.lines[0]);
                BOOST_CHECK_EQUAL(t.sample_size(1).height, i.lines[1]);
index c325537fa80a18908049188f5ade4b83ba9e7c76..5ed77219380f21cf5e692e153f6b0898f2accac8 100644 (file)
  */
 
 
-#include "lib/film.h"
-#include "lib/ffmpeg_content.h"
-#include "lib/dcp_content_type.h"
-#include "lib/ratio.h"
 #include "lib/audio_buffers.h"
-#include "lib/player.h"
-#include "lib/video_content.h"
-#include "lib/image_content.h"
-#include "lib/string_text_file_content.h"
-#include "lib/content_factory.h"
-#include "lib/dcp_content.h"
-#include "lib/text_content.h"
 #include "lib/butler.h"
 #include "lib/compose.hpp"
+#include "lib/content_factory.h"
 #include "lib/cross.h"
+#include "lib/dcp_content.h"
+#include "lib/dcp_content_type.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/film.h"
+#include "lib/image_content.h"
+#include "lib/player.h"
+#include "lib/ratio.h"
+#include "lib/string_text_file_content.h"
+#include "lib/text_content.h"
+#include "lib/video_content.h"
 #include "test.h"
 #include <boost/test/unit_test.hpp>
 #include <boost/algorithm/string.hpp>
@@ -48,7 +48,6 @@
 
 using std::cout;
 using std::list;
-using std::pair;
 using std::shared_ptr;
 using std::make_shared;
 using boost::bind;
@@ -84,7 +83,7 @@ BOOST_AUTO_TEST_CASE (player_silence_padding_test)
 
        accumulated = std::make_shared<AudioBuffers>(film->audio_channels(), 0);
 
-       auto player = std::make_shared<Player>(film);
+       auto player = std::make_shared<Player>(film, Image::Alignment::COMPACT);
        player->Audio.connect (bind (&accumulate, _1, _2));
        while (!player->pass ()) {}
        BOOST_REQUIRE (accumulated->frames() >= 48000);
@@ -164,7 +163,7 @@ BOOST_AUTO_TEST_CASE (player_subframe_test)
        /* Length should be rounded up from B's length to the next video frame */
        BOOST_CHECK (film->length() == DCPTime::from_frames(3 * 24 + 1, 24));
 
-       auto player = std::make_shared<Player>(film);
+       auto player = std::make_shared<Player>(film, Image::Alignment::COMPACT);
        player->setup_pieces ();
        BOOST_REQUIRE_EQUAL (player->_black._periods.size(), 1U);
        BOOST_CHECK (player->_black._periods.front() == DCPTimePeriod(DCPTime::from_frames(3 * 24, 24), DCPTime::from_frames(3 * 24 + 1, 24)));
@@ -206,7 +205,7 @@ BOOST_AUTO_TEST_CASE (player_interleave_test)
        film->examine_and_add_content (s);
        BOOST_REQUIRE (!wait_for_jobs ());
 
-       auto player = std::make_shared<Player>(film);
+       auto player = std::make_shared<Player>(film, Image::Alignment::COMPACT);
        player->Video.connect (bind (&video, _1, _2));
        player->Audio.connect (bind (&audio, _1, _2));
        video_frames = audio_frames = 0;
@@ -229,12 +228,12 @@ BOOST_AUTO_TEST_CASE (player_seek_test)
        BOOST_REQUIRE (!wait_for_jobs ());
        dcp->only_text()->set_use (true);
 
-       auto player = std::make_shared<Player>(film);
+       auto player = std::make_shared<Player>(film, Image::Alignment::COMPACT);
        player->set_fast ();
        player->set_always_burn_open_subtitles ();
        player->set_play_referenced ();
 
-       auto butler = std::make_shared<Butler>(film, player, AudioMapping(), 2, bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true);
+       auto butler = std::make_shared<Butler>(film, player, AudioMapping(), 2, bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, Image::Alignment::PADDED, true, false);
        butler->disable_audio();
 
        for (int i = 0; i < 10; ++i) {
@@ -242,7 +241,7 @@ BOOST_AUTO_TEST_CASE (player_seek_test)
                butler->seek (t, true);
                auto video = butler->get_video(true, 0);
                BOOST_CHECK_EQUAL(video.second.get(), t.get());
-               write_image(video.first->image(bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true), String::compose("build/test/player_seek_test_%1.png", i));
+               write_image(video.first->image(bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, true), String::compose("build/test/player_seek_test_%1.png", i));
                /* This 14.08 is empirically chosen (hopefully) to accept changes in rendering between the reference and a test machine
                   (17.10 and 16.04 seem to anti-alias a little differently) but to reject gross errors e.g. missing fonts or missing
                   text altogether.
@@ -261,12 +260,12 @@ BOOST_AUTO_TEST_CASE (player_seek_test2)
        BOOST_REQUIRE (!wait_for_jobs ());
        dcp->only_text()->set_use (true);
 
-       auto player = std::make_shared<Player>(film);
+       auto player = std::make_shared<Player>(film, Image::Alignment::COMPACT);
        player->set_fast ();
        player->set_always_burn_open_subtitles ();
        player->set_play_referenced ();
 
-       auto butler = std::make_shared<Butler>(film, player, AudioMapping(), 2, bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true);
+       auto butler = std::make_shared<Butler>(film, player, AudioMapping(), 2, bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, Image::Alignment::PADDED, true, false);
        butler->disable_audio();
 
        butler->seek(DCPTime::from_seconds(5), true);
@@ -277,7 +276,7 @@ BOOST_AUTO_TEST_CASE (player_seek_test2)
                auto video = butler->get_video(true, 0);
                BOOST_CHECK_EQUAL(video.second.get(), t.get());
                write_image(
-                       video.first->image(bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true), String::compose("build/test/player_seek_test2_%1.png", i)
+                       video.first->image(bind(PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, true), String::compose("build/test/player_seek_test2_%1.png", i)
                        );
                check_image(TestPaths::private_data() / String::compose("player_seek_test2_%1.png", i), String::compose("build/test/player_seek_test2_%1.png", i), 14.08);
        }
@@ -335,7 +334,7 @@ BOOST_AUTO_TEST_CASE (player_ignore_video_and_audio_test)
        text->only_text()->set_type (TextType::CLOSED_CAPTION);
        text->only_text()->set_use (true);
 
-       auto player = std::make_shared<Player>(film);
+       auto player = std::make_shared<Player>(film, Image::Alignment::COMPACT);
        player->set_ignore_video ();
        player->set_ignore_audio ();
 
@@ -355,9 +354,9 @@ BOOST_AUTO_TEST_CASE (player_trim_crash)
        film->examine_and_add_content (boon);
        BOOST_REQUIRE (!wait_for_jobs());
 
-       auto player = std::make_shared<Player>(film);
+       auto player = std::make_shared<Player>(film, Image::Alignment::COMPACT);
        player->set_fast ();
-       auto butler = std::make_shared<Butler>(film, player, AudioMapping(), 6, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, false, true);
+       auto butler = std::make_shared<Butler>(film, player, AudioMapping(), 6, bind(&PlayerVideo::force, _1, AV_PIX_FMT_RGB24), VideoRange::FULL, Image::Alignment::COMPACT, true, false);
 
        /* Wait for the butler to fill */
        dcpomatic_sleep_seconds (5);
index 4dab0cff1b5895c0e197e19c0db140092662757e..8417b30d2ec197111fb809958ee111b7fa5d2171 100644 (file)
@@ -364,9 +364,9 @@ double
 rms_error (boost::filesystem::path ref, boost::filesystem::path check)
 {
        FFmpegImageProxy ref_proxy (ref);
-       auto ref_image = ref_proxy.image().image;
+       auto ref_image = ref_proxy.image(Image::Alignment::COMPACT).image;
        FFmpegImageProxy check_proxy (check);
-       auto check_image = check_proxy.image().image;
+       auto check_image = check_proxy.image(Image::Alignment::COMPACT).image;
 
        BOOST_REQUIRE_EQUAL (ref_image->pixel_format(), check_image->pixel_format());
        AVPixelFormat const format = ref_image->pixel_format();
index ffe77c2b786ec2aa98000a254cecf288fa5a6df2..4ab5d0942923e55733cabc5045dfc7fcd7fcf949 100644 (file)
@@ -36,7 +36,6 @@
 
 using std::list;
 using std::make_shared;
-using std::shared_ptr;
 using std::string;
 using namespace dcpomatic;
 
@@ -197,7 +196,7 @@ BOOST_AUTO_TEST_CASE (player_time_calculation_test1)
        film->set_sequence (false);
        film->add_content (content);
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
 
        /* Position 0, no trim, content rate = DCP rate */
        content->set_position (film, DCPTime());
@@ -206,7 +205,7 @@ BOOST_AUTO_TEST_CASE (player_time_calculation_test1)
        film->set_video_frame_rate (24);
        player->setup_pieces ();
        BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
-       shared_ptr<Piece> piece = player->_pieces.front ();
+       auto piece = player->_pieces.front();
        BOOST_CHECK_EQUAL (player->dcp_to_content_video (piece, DCPTime ()), 0);
        BOOST_CHECK_EQUAL (player->dcp_to_content_video (piece, DCPTime::from_seconds (0.5)), 12);
        BOOST_CHECK_EQUAL (player->dcp_to_content_video (piece, DCPTime::from_seconds (3.0)), 72);
@@ -403,7 +402,7 @@ BOOST_AUTO_TEST_CASE (player_time_calculation_test2)
        film->set_sequence (false);
        film->add_content (content);
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
 
        /* Position 0, no trim, content rate = DCP rate */
        content->set_position (film, DCPTime());
@@ -412,7 +411,7 @@ BOOST_AUTO_TEST_CASE (player_time_calculation_test2)
        film->set_video_frame_rate (24);
        player->setup_pieces ();
        BOOST_REQUIRE_EQUAL (player->_pieces.size(), 1U);
-       shared_ptr<Piece> piece = player->_pieces.front ();
+       auto piece = player->_pieces.front ();
        BOOST_CHECK_EQUAL (player->content_video_to_dcp (piece, 0).get(), 0);
        BOOST_CHECK_EQUAL (player->content_video_to_dcp (piece, 12).get(), DCPTime::from_seconds(0.5).get());
        BOOST_CHECK_EQUAL (player->content_video_to_dcp (piece, 72).get(), DCPTime::from_seconds(3.0).get());
@@ -580,7 +579,7 @@ BOOST_AUTO_TEST_CASE (player_time_calculation_test3)
        film->set_sequence (false);
        film->add_content (content);
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
 
        /* Position 0, no trim, video/audio content rate = video/audio DCP rate */
        content->set_position (film, DCPTime());
index 0dc4962751eadb736c3a22cedfb5448125235708..af6c8b9e29611d7f011239881a0d7d6b6d770bfc 100644 (file)
@@ -89,7 +89,7 @@ BOOST_AUTO_TEST_CASE (upmixer_a_test)
        Ls = sf_open ("build/test/upmixer_a_test/Ls.wav", SFM_WRITE, &info);
        Rs = sf_open ("build/test/upmixer_a_test/Rs.wav", SFM_WRITE, &info);
 
-       auto player = make_shared<Player>(film);
+       auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
        player->Audio.connect (bind (&write, _1, _2));
        while (!player->pass()) {}
 
index b7e97604124d6aa3dd9bcf756a864a93b9cab5d4..4db48dd60071c83b5838323169eb4c16e361a26f 100644 (file)
@@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE (vf_test5)
        make_and_verify_dcp (vf, {dcp::VerificationNote::Code::EXTERNAL_ASSET});
 
        /* Check that the selected reel assets are right */
-       auto player = make_shared<Player>(vf);
+       auto player = make_shared<Player>(vf, Image::Alignment::COMPACT);
        auto a = player->get_reel_assets();
        BOOST_REQUIRE_EQUAL (a.size(), 4U);
        auto i = a.begin();
index be54cd3f90075f18837f75c059919fbd38336eac..ada9b602a3089ac5bfdc47e5f736ae1e5b374017 100644 (file)
@@ -54,7 +54,6 @@
 
 
 using std::min;
-using std::make_pair;
 using std::max;
 using std::pair;
 using std::string;
@@ -71,7 +70,7 @@ static
 shared_ptr<Image>
 grey_image (dcp::Size size, uint8_t pixel)
 {
-       auto grey = make_shared<Image>(AV_PIX_FMT_RGB24, size, true);
+       auto grey = make_shared<Image>(AV_PIX_FMT_RGB24, size, Image::Alignment::PADDED);
        for (int y = 0; y < size.height; ++y) {
                uint8_t* p = grey->data()[0] + y * grey->stride()[0];
                for (int x = 0; x < size.width; ++x) {
@@ -94,7 +93,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_image_full_range_not_changed)
        write_image (grey_image(size, grey_pixel), file);
 
        FFmpegImageProxy proxy (file);
-       ImageProxy::Result result = proxy.image ();
+       ImageProxy::Result result = proxy.image (Image::Alignment::COMPACT);
        BOOST_REQUIRE (!result.error);
 
        for (int y = 0; y < size.height; ++y) {
@@ -128,7 +127,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_image_video_range_expanded)
                BOOST_REQUIRE (!player->pass());
        }
 
-       auto image = player_video->image ([](AVPixelFormat f) { return f; }, VideoRange::FULL, true, false);
+       auto image = player_video->image ([](AVPixelFormat f) { return f; }, VideoRange::FULL, false);
 
        for (int y = 0; y < size.height; ++y) {
                uint8_t* p = image->data()[0] + y * image->stride()[0];
@@ -214,7 +213,7 @@ pixel_range (shared_ptr<const Film> film, shared_ptr<const Content> content)
                BOOST_REQUIRE (!decoder->pass());
        }
 
-       return pixel_range (content_video->image->image().image);
+       return pixel_range (content_video->image->image(Image::Alignment::COMPACT).image);
 }