Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add website example source files to the CMake build for validation #1501

Merged
merged 1 commit into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,22 @@ define_manpage(exrmultipart "combine or split multipart exr images")
define_manpage(exrmultiview "convert between single/multi-view exr images")
define_manpage(exrstdattr "set exr image metadata")

add_subdirectory(src)

add_custom_target(docs ALL DEPENDS
${SPHINX_INDEX_FILE}
${DOXYGEN_INDEX_FILE}
${TEST_IMAGE_INDEX_FILE}
${manpage_files})
${manpage_files}
${CMAKE_BINARY_DIR}/bin/docs_src)

# Add a target to install the manpages, but *not* the html

if(INSTALL_DOCS)
include(GNUInstallDirs)
install(DIRECTORY ${MANPAGE_OUTPUT_DIR}/man1 DESTINATION ${CMAKE_INSTALL_MANDIR})
endif()

# build the example code to verify that it compiles


233 changes: 23 additions & 210 deletions docs/ReadingAndWritingImageFiles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -214,22 +214,9 @@ is indicated by the display window, ``(0,0) - (width-1, height-1)``, and
the data window specifies the region for which valid pixel data exist.
Only the pixels in the data window are stored in the file.

.. code-block::
.. literalinclude:: src/writeRgba2.cpp
:linenos:

void
writeRgba2 (const char fileName[],
const Rgba *pixels,
int width,
int height,
const Box2i &dataWindow)
{
Box2i displayWindow (V2i (0, 0), V2i (width - 1, height - 1));
RgbaOutputFile file (fileName, displayWindow, dataWindow, WRITE_RGBA);
file.setFrameBuffer (pixels, 1, width);
file.writePixels (dataWindow.max.y - dataWindow.min.y + 1);
}

The code above is similar to that in `Writing an RGBA Image File`_, where the
whole image was stored in the file. Two things are different, however: When the
``RgbaOutputFile`` object is created, the data window and the display window are
Expand Down Expand Up @@ -682,7 +669,7 @@ an array of structs, which are defined like this:

.. code-block::

typedef struct GZ
struct GZ
{
half g;
float z;
Expand Down Expand Up @@ -1121,7 +1108,7 @@ demonstrates how to write a deep scan line file with two channels:

The size of the image is ``width`` by ``height`` pixels.

.. literalinclude:: src/writeDeepScanlineFile.cpp
.. literalinclude:: src/writeDeepScanLineFile.cpp
:language: c++
:linenos:

Expand Down Expand Up @@ -1171,7 +1158,7 @@ Reading a Deep Scan Line File

An example of reading a deep scan line file created by previous code.

.. literalinclude:: src/readDeepScanlineFile.cpp
.. literalinclude:: src/readDeepScanLineFile.cpp
:language: c++
:linenos:

Expand All @@ -1196,20 +1183,20 @@ Writing a Deep Tiled File
This example creates an deep tiled file with two channels. It
demonstrates how to write a deep tiled file with two channels:

1. type ``FLOAT``, is called Z, and is used for storing sample depth, and
2. type ``HALF``, is called A and is used for storing sample opacity.
1. ``Z``, of type ``FLOAT``, and is used for storing sample depth, and
2. ``A``, type ``HALF``, and is used for storing sample opacity.

The size of the image is ``width`` by ``height`` pixels.

.. literalinclude:: src/writeDeepTiledFile.cpp
:language: c++
:linenos:

Here, getSampleCountForTile is a user-supplied function that sets each
item in sampleCount array to the correct sampleCount for each pixel in
the tile, and getSampleDataForTile is a user-supplied function that
set the pointers in dataZ and dataA arrays to point to the correct
data
Here, ``getSampleCountForTile`` is a user-supplied function that sets
each item in ``sampleCount`` array to the correct ``sampleCount`` for
each pixel in the tile, and ``getSampleDataForTile`` is a
user-supplied function that set the pointers in ``dataZ`` and
``dataA`` arrays to point to the correct data

The interface for deep tiled files is similar to tiled files. The
differences are:
Expand Down Expand Up @@ -1400,115 +1387,43 @@ stdio file (of type ``FILE``) that has already been opened. To do
this, we derive a new class, ``C_IStream``, from ``IStream``. The
declaration of class ``IStream`` looks like this:

.. code-block::
.. literalinclude:: src/IStream.cpp
:linenos:

class IStream
{
public:
virtual ~IStream ();

virtual bool read (char c[], int n) = 0;
virtual uint64_t tellg () = 0;
virtual void seekg (uint64_t pos) = 0;
virtual void clear ();
const char * fileName () const;
virtual bool isMemoryMapped () const;
virtual char * readMemoryMapped (int n);

protected:
IStream (const char fileName[]);
private:
...
};

Our derived class needs a public constructor, and it must override four
methods:

.. code-block::
.. literalinclude:: src/C_IStream.cpp
:linenos:

class C_IStream: public IStream
{
public:
C_IStream (FILE *file, const char fileName[]):
IStream (fileName), _file (file) {}

virtual bool read (char c[], int n);
virtual uint64_t tellg ();
virtual void seekg (uint64_t pos);
virtual void clear ();

private:

FILE * _file;
};

``read(c,n)`` reads ``n`` bytes from the file, and stores them in
array ``c``. If reading hits the end of the file before ``n`` bytes
have been read, or if an I/O error occurs, ``read(c,n)`` throws an
exception. If ``read(c,n)`` hits the end of the file after reading
``n`` bytes, it returns ``false``, otherwise it returns ``true``:

.. code-block::
.. literalinclude:: src/C_IStream_read.cpp
:linenos:

bool
C_IStream::read (char c[], int n)
{
if (n != fread (c, 1, n, _file))
{
// fread() failed, but the return value does not distinguish
// between I/O errors and end of file, so we call ferror() to
// determine what happened.

if (ferror (_file))
Iex::throwErrnoExc();
else
throw Iex::InputExc ("Unexpected end of file.");
}

return !feof (_file);
}

``tellg()`` returns the current reading position, in bytes, from the
beginning of the file. The next ``read()`` call will begin reading at
the indicated position:

.. code-block::
.. literalinclude:: src/C_IStream_tellg.cpp
:linenos:

uint64_t
C_IStream::tellg ()
{
return ftell (_file);
}

``seekg(pos)`` sets the current reading position to ``pos`` bytes from
the beginning of the file:

.. code-block::
.. literalinclude:: src/C_IStream_seekg.cpp
:linenos:

void
C_IStream::seekg (uint64_t pos)
{
clearerr (_file);
fseek (_file, pos, SEEK_SET);
}

``clear()`` clears any error flags that may be set on the file after a
``read()`` or ``seekg()`` operation has failed:

.. code-block::
.. literalinclude:: src/C_IStream_clear.cpp
:linenos:

void
C_IStream::clear ()
{
clearerr (_file);
}

In order to read an RGBA image from an open C stdio file, we first
make a ``C_IStream`` object. Then we create an ``RgbaInputFile``,
passing the ``C_IStream`` instead of a file name to the
Expand Down Expand Up @@ -1543,149 +1458,47 @@ input. In order to do this, a derived class must override two virtual
functions, ``isMemoryMapped()`` and ``readMemoryMapped()``, in
addition to the functions needed for regular, non-memory-mapped input:

.. code-block::
.. literalinclude:: src/MemoryMappedIStream.cpp
:linenos:

class MemoryMappedIStream: public IStream
{
public:
MemoryMappedIStream (const char fileName[]);

virtual ~MemoryMappedIStream ();

virtual bool isMemoryMapped () const;
virtual char * readMemoryMapped (int n);
virtual bool read (char c[], int n);
virtual uint64_t tellg ();

virtual void seekg (uint64_t pos);

private:

char * _buffer;
uint64_t _fileLength;
uint64_t _readPosition;
};

The constructor for class ``MemoryMappedIStream`` maps the contents of
the input file into the program's address space. Memory mapping is not
portable across operating systems. The example shown here uses the
POSIX ``mmap()`` system call. On Windows files can be memory-mapped by
calling ``CreateFileMapping()`` and ``MapViewOfFile()``:

.. code-block::
.. literalinclude:: src/MemoryMappedIStream_constructor.cpp
:linenos:

MemoryMappedIStream::MemoryMappedIStream (const char fileName[])
: IStream (fileName),
_buffer (0),
_fileLength (0),
_readPosition (0)
{
int file = open (fileName, O_RDONLY);

if (file < 0)
THROW_ERRNO ("Cannot open file \"" << fileName << "\".");

struct stat stat;
fstat (file, &stat);

_fileLength = stat.st_size;

_buffer = (char *) mmap (0, _fileLength, PROT_READ, MAP_PRIVATE, file, 0);

close (file);

if (_buffer == MAP_FAILED)
THROW_ERRNO ("Cannot memory-map file \"" << fileName << "\".");
}

The destructor frees the address range associated with the file by
un-mapping the file. The POSIX version shown here uses ``munmap()``. A
Windows version would call ``UnmapViewOfFile()`` and
``CloseHandle()``:

.. code-block::
.. literalinclude:: src/MemoryMappedIStream_destructor.cpp
:linenos:

MemoryMappedIStream::~MemoryMappedIStream ()
{
munmap (_buffer, _fileLength);
}

Function ``isMemoryMapped()`` returns ``true`` to indicate that
memory-mapped input is supported. This allows the OpenEXR library to
call ``readMemoryMapped()`` instead of ``read()``:

.. code-block::
.. literalinclude:: src/MemoryMappedIStream_isMemoryMapped.cpp
:linenos:

bool
MemoryMappedIStream::isMemoryMapped () const
{
return true;
}

``readMemoryMapped()`` is analogous to ``read()``, but instead of
copying data into a buffer supplied by the caller,
``readMemoryMapped()`` returns a pointer into the memory-mapped file,
thus avoiding the copy operation:

.. code-block::
.. literalinclude:: src/MemoryMappedIStream_readMemoryMapped.cpp
:linenos:

char *
MemoryMappedIStream::readMemoryMapped (int n)
{
if (_readPosition >= _fileLength)
throw Iex::InputExc ("Unexpected end of file.");

if (_readPosition + n > _fileLength)
throw Iex::InputExc ("Reading past end of file.");

char *data = _buffer + _readPosition;

_readPosition += n;

return data;

}

The ``MemoryMappedIStream`` class must also implement the regular ``read()``
function, as well as ``tellg()`` and ``seekg()``:

.. code-block::
.. literalinclude:: src/MemoryMappedIStream_read.cpp
:linenos:

bool
MemoryMappedIStream::read (char c[], int n)
{
if (_readPosition >= _fileLength)
throw Iex::InputExc ("Unexpected end of file.");

if (_readPosition + n > _fileLength)
throw Iex::InputExc ("Reading past end of file.");

memcpy (c, _buffer + _readPosition, n);

_readPosition += n;

return _readPosition < _fileLength;

}

uint64_t
MemoryMappedIStream::tellg ()
{
return _readPosition;
}

void
MemoryMappedIStream::seekg (uint64_t pos)
{
_readPosition = pos;
}

Class ``MemoryMappedIStream`` does not need a ``clear()``
function. Since the memory-mapped file has no error flags that need to
be cleared, the ``clear()`` method provided by class ``IStream``,
Expand Down
11 changes: 11 additions & 0 deletions docs/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.

add_executable(docs_src all.cpp)
target_link_libraries(docs_src OpenEXR::OpenEXR)
set_target_properties(docs_src PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
if(WIN32 AND BUILD_SHARED_LIBS)
target_compile_definitions(docs_src PRIVATE OPENEXR_DLL)
endif()
Loading