2016-01-04 08:45:40 -05:00
|
|
|
|
#pragma once
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
|
|
#include "EmberCommonPch.h"
|
|
|
|
|
|
|
|
|
|
#define PNG_COMMENT_MAX 8
|
2017-08-22 23:13:25 -04:00
|
|
|
|
static std::recursive_mutex fileCs;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Write a JPEG file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="filename">The full path and name of the file</param>
|
|
|
|
|
/// <param name="image">Pointer to the image data to write</param>
|
|
|
|
|
/// <param name="width">Width of the image in pixels</param>
|
|
|
|
|
/// <param name="height">Height of the image in pixels</param>
|
|
|
|
|
/// <param name="quality">The quality to use</param>
|
|
|
|
|
/// <param name="enableComments">True to embed comments, else false</param>
|
|
|
|
|
/// <param name="comments">The comment string to embed</param>
|
|
|
|
|
/// <param name="id">Id of the author</param>
|
|
|
|
|
/// <param name="url">Url of the author</param>
|
|
|
|
|
/// <param name="nick">Nickname of the author</param>
|
|
|
|
|
/// <returns>True if success, else false</returns>
|
--User changes
-Add support for multiple GPU devices.
--These options are present in the command line and in Fractorium.
-Change scheme of specifying devices from platform,device to just total device index.
--Single number on the command line.
--Change from combo boxes for device selection to a table of all devices in Fractorium.
-Temporal samples defaults to 100 instead of 1000 which was needless overkill.
--Bug fixes
-EmberAnimate, EmberRender, FractoriumSettings, FinalRenderDialog: Fix wrong order of arguments to Clamp() when assigning thread priority.
-VariationsDC.h: Fix NVidia OpenCL compilation error in DCTriangleVariation.
-FractoriumXformsColor.cpp: Checking for null pixmap pointer is not enough, must also check if the underlying buffer is null via call to QPixmap::isNull().
--Code changes
-Ember.h: Add case for FLAME_MOTION_NONE and default in ApplyFlameMotion().
-EmberMotion.h: Call base constructor.
-EmberPch.h: #pragma once only on Windows.
-EmberToXml.h:
--Handle different types of exceptions.
--Add default cases to ToString().
-Isaac.h: Remove unused variable in constructor.
-Point.h: Call base constructor in Color().
-Renderer.h/cpp:
--Add bool to Alloc() to only allocate memory for the histogram. Needed for multi-GPU.
--Make CoordMap() return a const ref, not a pointer.
-SheepTools.h:
--Use 64-bit types like the rest of the code already does.
--Fix some comment misspellings.
-Timing.h: Make BeginTime(), EndTime(), ElapsedTime() and Format() be const functions.
-Utils.h:
--Add new functions Equal() and Split().
--Handle more exception types in ReadFile().
--Get rid of most legacy blending of C and C++ argument parsing.
-XmlToEmber.h:
--Get rid of most legacy blending of C and C++ code from flam3.
--Remove some unused variables.
-EmberAnimate:
--Support multi-GPU processing that alternates full frames between devices.
--Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
--Remove bucketT template parameter, and hard code float in its place.
--If a render fails, exit since there is no point in continuing an animation with a missing frame.
--Pass variables to threaded save better, which most likely fixes a very subtle bug that existed before.
--Remove some unused variables.
-EmberGenome, EmberRender:
--Support multi-GPU processing that alternates full frames between devices.
--Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
--Remove bucketT template parameter, and hard code float in its place.
-EmberRender:
--Support multi-GPU processing that alternates full frames between devices.
--Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
--Remove bucketT template parameter, and hard code float in its place.
--Only print values when not rendering with OpenCL, since they're always 0 in that case.
-EmberCLPch.h:
--#pragma once only on Windows.
--#include <atomic>.
-IterOpenCLKernelCreator.h: Add new kernel for summing two histograms. This is needed for multi-GPU.
-OpenCLWrapper.h:
--Move all OpenCL info related code into its own class OpenCLInfo.
--Add members to cache the values of global memory size and max allocation size.
-RendererCL.h/cpp:
--Redesign to accomodate multi-GPU.
--Constructor now takes a vector of devices.
--Remove DumpErrorReport() function, it's handled in the base.
--ClearBuffer(), ReadPoints(), WritePoints(), ReadHist() and WriteHist() now optionally take a device index as a parameter.
--MakeDmap() override and m_DmapCL member removed because it no longer applies since the histogram is always float since the last commit.
--Add new function SumDeviceHist() to sum histograms from two devices by first copying to a temporary on the host, then a temporary on the device, then summing.
--m_Calls member removed, as it's now per-device.
--OpenCLWrapper removed.
--m_Seeds member is now a vector of vector of seeds, to accomodate a separate and different array of seeds for each device.
--Added member m_Devices, a vector of unique_ptr of RendererCLDevice.
-EmberCommon.h
--Added Devices() function to convert from a vector of device indices to a vector of platform,device indices.
--Changed CreateRenderer() to accept a vector of devices to create a single RendererCL which will split work across multiple devices.
--Added CreateRenderers() function to accept a vector of devices to create multiple RendererCL, each which will render on a single device.
--Add more comments to some existing functions.
-EmberCommonPch.h: #pragma once only on Windows.
-EmberOptions.h:
--Remove --platform option, it's just sequential device number now with the --device option.
--Make --out be OPT_USE_RENDER instead of OPT_RENDER_ANIM since it's an error condition when animating. It makes no sense to write all frames to a single image.
--Add Devices() function to parse comma separated --device option string and return a vector of device indices.
--Make int and uint types be 64-bit, so intmax_t and size_t.
--Make better use of macros.
-JpegUtils.h: Make string parameters to WriteJpeg() and WritePng() be const ref.
-All project files: Turn off buffer security check option in Visual Studio (/Gs-)
-deployment.pri: Remove the line OTHER_FILES +=, it's pointless and was causing problems.
-Ember.pro, EmberCL.pro: Add CONFIG += plugin, otherwise it wouldn't link.
-EmberCL.pro: Add new files for multi-GPU support.
-build_all.sh: use -j4 and QMAKE=${QMAKE:/usr/bin/qmake}
-shared_settings.pri:
-Add version string.
-Remove old DESTDIR definitions.
-Add the following lines or else nothing would build:
CONFIG(release, debug|release) {
CONFIG += warn_off
DESTDIR = ../../../Bin/release
}
CONFIG(debug, debug|release) {
DESTDIR = ../../../Bin/debug
}
QMAKE_POST_LINK += $$quote(cp --update ../../../Data/flam3-palettes.xml $${DESTDIR}$$escape_expand(\n\t))
LIBS += -L/usr/lib -lpthread
-AboutDialog.ui: Another futile attempt to make it look correct on Linux.
-FinalRenderDialog.h/cpp:
--Add support for multi-GPU.
--Change from combo boxes for device selection to a table of all devices.
--Ensure device selection makes sense.
--Remove "FinalRender" prefix of various function names, it's implied given the context.
-FinalRenderEmberController.h/cpp:
--Add support for multi-GPU.
--Change m_FinishedImageCount to be atomic.
--Move CancelRender() from the base to FinalRenderEmberController<T>.
--Refactor RenderComplete() to omit any progress related functionality or image saving since it can be potentially ran in a thread.
--Consolidate setting various renderer fields into SyncGuiToRenderer().
-Fractorium.cpp: Allow for resizing of the options dialog to show the entire device table.
-FractoriumCommon.h: Add various functions to handle a table showing the available OpenCL devices on the system.
-FractoriumEmberController.h/cpp: Remove m_FinalImageIndex, it's no longer needed.
-FractoriumRender.cpp: Scale the interactive sub batch count and quality by the number of devices used.
-FractoriumSettings.h/cpp:
--Temporal samples defaults to 100 instead of 1000 which was needless overkill.
--Add multi-GPU support, remove old device,platform pair.
-FractoriumToolbar.cpp: Disable OpenCL toolbar button if there are no devices present on the system.
-FractoriumOptionsDialog.h/cpp:
--Add support for multi-GPU.
--Consolidate more assignments in DataToGui().
--Enable/disable CPU/OpenCL items in response to OpenCL checkbox event.
-Misc: Convert almost everything to size_t for unsigned, intmax_t for signed.
2015-09-12 21:33:45 -04:00
|
|
|
|
static bool WriteJpeg(const char* filename, byte* image, size_t width, size_t height, int quality, bool enableComments, const EmberImageComments& comments, const string& id, const string& url, const string& nick)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
bool b = false;
|
|
|
|
|
FILE* file;
|
2017-08-22 23:13:25 -04:00
|
|
|
|
errno_t fileResult;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2017-08-22 23:13:25 -04:00
|
|
|
|
//Just to be extra safe.
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
rlg l(fileCs);
|
|
|
|
|
fileResult = fopen_s(&file, filename, "wb");
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fileResult == 0)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
jpeg_error_mgr jerr;
|
|
|
|
|
jpeg_compress_struct info;
|
2016-12-05 22:04:33 -05:00
|
|
|
|
string nickString, urlString, idString;
|
|
|
|
|
string bvString, niString, rtString;
|
|
|
|
|
string genomeString, verString;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//Create the mandatory comment strings.
|
2016-12-05 22:04:33 -05:00
|
|
|
|
ostringstream os;
|
|
|
|
|
os << "genome: " << comments.m_Genome; genomeString = os.str(); os.str("");
|
|
|
|
|
os << "error_rate: " << comments.m_Badvals; bvString = os.str(); os.str("");
|
|
|
|
|
os << "samples: " << comments.m_NumIters; niString = os.str(); os.str("");
|
|
|
|
|
os << "time: " << comments.m_Runtime; rtString = os.str(); os.str("");
|
|
|
|
|
os << "version: " << EmberVersion(); verString = os.str(); os.str("");
|
2014-07-08 03:11:14 -04:00
|
|
|
|
info.err = jpeg_std_error(&jerr);
|
|
|
|
|
jpeg_create_compress(&info);
|
|
|
|
|
jpeg_stdio_dest(&info, file);
|
|
|
|
|
info.in_color_space = JCS_RGB;
|
|
|
|
|
info.input_components = 3;
|
2014-12-07 02:51:44 -05:00
|
|
|
|
info.image_width = JDIMENSION(width);
|
|
|
|
|
info.image_height = JDIMENSION(height);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
jpeg_set_defaults(&info);
|
2019-05-16 21:06:09 -04:00
|
|
|
|
#if defined(_WIN32) || defined(__APPLE__)
|
2016-03-01 20:26:45 -05:00
|
|
|
|
jpeg_set_quality(&info, quality, static_cast<boolean>(TRUE));
|
|
|
|
|
jpeg_start_compress(&info, static_cast<boolean>(TRUE));
|
|
|
|
|
//Win32:TRUE is defined in MSVC2013\Windows Kits\8.1\Include\shared\minwindef.h:"#define TRUE 1"
|
|
|
|
|
//cast from int to boolean in External/libjpeg/jmorecfg.h:"typedef enum { FALSE = 0, TRUE =1 } boolean;"
|
|
|
|
|
#else
|
|
|
|
|
jpeg_set_quality(&info, quality, TRUE);
|
|
|
|
|
jpeg_start_compress(&info, TRUE);
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//Write comments to jpeg.
|
|
|
|
|
if (enableComments)
|
|
|
|
|
{
|
2016-12-05 22:04:33 -05:00
|
|
|
|
string s;
|
|
|
|
|
jpeg_write_marker(&info, JPEG_COM, reinterpret_cast<const byte*>(verString.c_str()), uint(verString.size()));
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
|
|
if (nick != "")
|
|
|
|
|
{
|
2016-12-05 22:04:33 -05:00
|
|
|
|
os.str("");
|
|
|
|
|
os << "nickname: " << nick;
|
|
|
|
|
s = os.str();
|
|
|
|
|
jpeg_write_marker(&info, JPEG_COM, reinterpret_cast<const byte*>(s.c_str()), uint(s.size()));
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (url != "")
|
|
|
|
|
{
|
2016-12-05 22:04:33 -05:00
|
|
|
|
os.str("");
|
|
|
|
|
os << "url: " << url;
|
|
|
|
|
s = os.str();
|
|
|
|
|
jpeg_write_marker(&info, JPEG_COM, reinterpret_cast<const byte*>(s.c_str()), uint(s.size()));
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
2014-12-05 21:30:46 -05:00
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
if (id != "")
|
|
|
|
|
{
|
2016-12-05 22:04:33 -05:00
|
|
|
|
os.str("");
|
|
|
|
|
os << "id: " << id;
|
|
|
|
|
s = os.str();
|
|
|
|
|
jpeg_write_marker(&info, JPEG_COM, reinterpret_cast<const byte*>(s.c_str()), uint(s.size()));
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
2016-12-05 22:04:33 -05:00
|
|
|
|
jpeg_write_marker(&info, JPEG_COM, reinterpret_cast<const byte*>(bvString.c_str()), uint(bvString.size()));
|
|
|
|
|
jpeg_write_marker(&info, JPEG_COM, reinterpret_cast<const byte*>(niString.c_str()), uint(niString.size()));
|
|
|
|
|
jpeg_write_marker(&info, JPEG_COM, reinterpret_cast<const byte*>(rtString.c_str()), uint(rtString.size()));
|
|
|
|
|
jpeg_write_marker(&info, JPEG_COM, reinterpret_cast<const byte*>(genomeString.c_str()), uint(genomeString.size()));
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < height; i++)
|
|
|
|
|
{
|
|
|
|
|
JSAMPROW row_pointer[1];
|
2016-12-05 22:04:33 -05:00
|
|
|
|
row_pointer[0] = image + (3 * width * i);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
jpeg_write_scanlines(&info, row_pointer, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jpeg_finish_compress(&info);
|
|
|
|
|
jpeg_destroy_compress(&info);
|
|
|
|
|
fclose(file);
|
|
|
|
|
b = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Write a PNG file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="filename">The full path and name of the file</param>
|
|
|
|
|
/// <param name="image">Pointer to the image data to write</param>
|
|
|
|
|
/// <param name="width">Width of the image in pixels</param>
|
|
|
|
|
/// <param name="height">Height of the image in pixels</param>
|
|
|
|
|
/// <param name="bytesPerChannel">Bytes per channel, 1 or 2.</param>
|
|
|
|
|
/// <param name="enableComments">True to embed comments, else false</param>
|
|
|
|
|
/// <param name="comments">The comment string to embed</param>
|
|
|
|
|
/// <param name="id">Id of the author</param>
|
|
|
|
|
/// <param name="url">Url of the author</param>
|
|
|
|
|
/// <param name="nick">Nickname of the author</param>
|
|
|
|
|
/// <returns>True if success, else false</returns>
|
--User changes
-Add support for multiple GPU devices.
--These options are present in the command line and in Fractorium.
-Change scheme of specifying devices from platform,device to just total device index.
--Single number on the command line.
--Change from combo boxes for device selection to a table of all devices in Fractorium.
-Temporal samples defaults to 100 instead of 1000 which was needless overkill.
--Bug fixes
-EmberAnimate, EmberRender, FractoriumSettings, FinalRenderDialog: Fix wrong order of arguments to Clamp() when assigning thread priority.
-VariationsDC.h: Fix NVidia OpenCL compilation error in DCTriangleVariation.
-FractoriumXformsColor.cpp: Checking for null pixmap pointer is not enough, must also check if the underlying buffer is null via call to QPixmap::isNull().
--Code changes
-Ember.h: Add case for FLAME_MOTION_NONE and default in ApplyFlameMotion().
-EmberMotion.h: Call base constructor.
-EmberPch.h: #pragma once only on Windows.
-EmberToXml.h:
--Handle different types of exceptions.
--Add default cases to ToString().
-Isaac.h: Remove unused variable in constructor.
-Point.h: Call base constructor in Color().
-Renderer.h/cpp:
--Add bool to Alloc() to only allocate memory for the histogram. Needed for multi-GPU.
--Make CoordMap() return a const ref, not a pointer.
-SheepTools.h:
--Use 64-bit types like the rest of the code already does.
--Fix some comment misspellings.
-Timing.h: Make BeginTime(), EndTime(), ElapsedTime() and Format() be const functions.
-Utils.h:
--Add new functions Equal() and Split().
--Handle more exception types in ReadFile().
--Get rid of most legacy blending of C and C++ argument parsing.
-XmlToEmber.h:
--Get rid of most legacy blending of C and C++ code from flam3.
--Remove some unused variables.
-EmberAnimate:
--Support multi-GPU processing that alternates full frames between devices.
--Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
--Remove bucketT template parameter, and hard code float in its place.
--If a render fails, exit since there is no point in continuing an animation with a missing frame.
--Pass variables to threaded save better, which most likely fixes a very subtle bug that existed before.
--Remove some unused variables.
-EmberGenome, EmberRender:
--Support multi-GPU processing that alternates full frames between devices.
--Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
--Remove bucketT template parameter, and hard code float in its place.
-EmberRender:
--Support multi-GPU processing that alternates full frames between devices.
--Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
--Remove bucketT template parameter, and hard code float in its place.
--Only print values when not rendering with OpenCL, since they're always 0 in that case.
-EmberCLPch.h:
--#pragma once only on Windows.
--#include <atomic>.
-IterOpenCLKernelCreator.h: Add new kernel for summing two histograms. This is needed for multi-GPU.
-OpenCLWrapper.h:
--Move all OpenCL info related code into its own class OpenCLInfo.
--Add members to cache the values of global memory size and max allocation size.
-RendererCL.h/cpp:
--Redesign to accomodate multi-GPU.
--Constructor now takes a vector of devices.
--Remove DumpErrorReport() function, it's handled in the base.
--ClearBuffer(), ReadPoints(), WritePoints(), ReadHist() and WriteHist() now optionally take a device index as a parameter.
--MakeDmap() override and m_DmapCL member removed because it no longer applies since the histogram is always float since the last commit.
--Add new function SumDeviceHist() to sum histograms from two devices by first copying to a temporary on the host, then a temporary on the device, then summing.
--m_Calls member removed, as it's now per-device.
--OpenCLWrapper removed.
--m_Seeds member is now a vector of vector of seeds, to accomodate a separate and different array of seeds for each device.
--Added member m_Devices, a vector of unique_ptr of RendererCLDevice.
-EmberCommon.h
--Added Devices() function to convert from a vector of device indices to a vector of platform,device indices.
--Changed CreateRenderer() to accept a vector of devices to create a single RendererCL which will split work across multiple devices.
--Added CreateRenderers() function to accept a vector of devices to create multiple RendererCL, each which will render on a single device.
--Add more comments to some existing functions.
-EmberCommonPch.h: #pragma once only on Windows.
-EmberOptions.h:
--Remove --platform option, it's just sequential device number now with the --device option.
--Make --out be OPT_USE_RENDER instead of OPT_RENDER_ANIM since it's an error condition when animating. It makes no sense to write all frames to a single image.
--Add Devices() function to parse comma separated --device option string and return a vector of device indices.
--Make int and uint types be 64-bit, so intmax_t and size_t.
--Make better use of macros.
-JpegUtils.h: Make string parameters to WriteJpeg() and WritePng() be const ref.
-All project files: Turn off buffer security check option in Visual Studio (/Gs-)
-deployment.pri: Remove the line OTHER_FILES +=, it's pointless and was causing problems.
-Ember.pro, EmberCL.pro: Add CONFIG += plugin, otherwise it wouldn't link.
-EmberCL.pro: Add new files for multi-GPU support.
-build_all.sh: use -j4 and QMAKE=${QMAKE:/usr/bin/qmake}
-shared_settings.pri:
-Add version string.
-Remove old DESTDIR definitions.
-Add the following lines or else nothing would build:
CONFIG(release, debug|release) {
CONFIG += warn_off
DESTDIR = ../../../Bin/release
}
CONFIG(debug, debug|release) {
DESTDIR = ../../../Bin/debug
}
QMAKE_POST_LINK += $$quote(cp --update ../../../Data/flam3-palettes.xml $${DESTDIR}$$escape_expand(\n\t))
LIBS += -L/usr/lib -lpthread
-AboutDialog.ui: Another futile attempt to make it look correct on Linux.
-FinalRenderDialog.h/cpp:
--Add support for multi-GPU.
--Change from combo boxes for device selection to a table of all devices.
--Ensure device selection makes sense.
--Remove "FinalRender" prefix of various function names, it's implied given the context.
-FinalRenderEmberController.h/cpp:
--Add support for multi-GPU.
--Change m_FinishedImageCount to be atomic.
--Move CancelRender() from the base to FinalRenderEmberController<T>.
--Refactor RenderComplete() to omit any progress related functionality or image saving since it can be potentially ran in a thread.
--Consolidate setting various renderer fields into SyncGuiToRenderer().
-Fractorium.cpp: Allow for resizing of the options dialog to show the entire device table.
-FractoriumCommon.h: Add various functions to handle a table showing the available OpenCL devices on the system.
-FractoriumEmberController.h/cpp: Remove m_FinalImageIndex, it's no longer needed.
-FractoriumRender.cpp: Scale the interactive sub batch count and quality by the number of devices used.
-FractoriumSettings.h/cpp:
--Temporal samples defaults to 100 instead of 1000 which was needless overkill.
--Add multi-GPU support, remove old device,platform pair.
-FractoriumToolbar.cpp: Disable OpenCL toolbar button if there are no devices present on the system.
-FractoriumOptionsDialog.h/cpp:
--Add support for multi-GPU.
--Consolidate more assignments in DataToGui().
--Enable/disable CPU/OpenCL items in response to OpenCL checkbox event.
-Misc: Convert almost everything to size_t for unsigned, intmax_t for signed.
2015-09-12 21:33:45 -04:00
|
|
|
|
static bool WritePng(const char* filename, byte* image, size_t width, size_t height, size_t bytesPerChannel, bool enableComments, const EmberImageComments& comments, const string& id, const string& url, const string& nick)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
bool b = false;
|
|
|
|
|
FILE* file;
|
2017-08-22 23:13:25 -04:00
|
|
|
|
errno_t fileResult;
|
|
|
|
|
|
|
|
|
|
//Just to be extra safe.
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
rlg l(fileCs);
|
|
|
|
|
fileResult = fopen_s(&file, filename, "wb");
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2017-08-22 23:13:25 -04:00
|
|
|
|
if (fileResult == 0)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
png_structp png_ptr;
|
|
|
|
|
png_infop info_ptr;
|
|
|
|
|
png_text text[PNG_COMMENT_MAX];
|
|
|
|
|
size_t i;
|
2015-03-21 18:27:37 -04:00
|
|
|
|
glm::uint16 testbe = 1;
|
2014-12-06 00:05:09 -05:00
|
|
|
|
vector<byte*> rows(height);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
|
2017-07-22 16:43:35 -04:00
|
|
|
|
text[0].key = const_cast<png_charp>("ember_version");
|
2014-12-07 02:51:44 -05:00
|
|
|
|
text[0].text = const_cast<png_charp>(EmberVersion());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
text[1].compression = PNG_TEXT_COMPRESSION_NONE;
|
2017-07-22 16:43:35 -04:00
|
|
|
|
text[1].key = const_cast<png_charp>("ember_nickname");
|
2014-12-07 02:51:44 -05:00
|
|
|
|
text[1].text = const_cast<png_charp>(nick.c_str());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
text[2].compression = PNG_TEXT_COMPRESSION_NONE;
|
2017-07-22 16:43:35 -04:00
|
|
|
|
text[2].key = const_cast<png_charp>("ember_url");
|
2014-12-07 02:51:44 -05:00
|
|
|
|
text[2].text = const_cast<png_charp>(url.c_str());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
text[3].compression = PNG_TEXT_COMPRESSION_NONE;
|
2017-07-22 16:43:35 -04:00
|
|
|
|
text[3].key = const_cast<png_charp>("ember_id");
|
2014-12-07 02:51:44 -05:00
|
|
|
|
text[3].text = const_cast<png_charp>(id.c_str());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
text[4].compression = PNG_TEXT_COMPRESSION_NONE;
|
2017-07-22 16:43:35 -04:00
|
|
|
|
text[4].key = const_cast<png_charp>("ember_error_rate");
|
2014-12-07 02:51:44 -05:00
|
|
|
|
text[4].text = const_cast<png_charp>(comments.m_Badvals.c_str());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
text[5].compression = PNG_TEXT_COMPRESSION_NONE;
|
2017-07-22 16:43:35 -04:00
|
|
|
|
text[5].key = const_cast<png_charp>("ember_samples");
|
2014-12-07 02:51:44 -05:00
|
|
|
|
text[5].text = const_cast<png_charp>(comments.m_NumIters.c_str());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
text[6].compression = PNG_TEXT_COMPRESSION_NONE;
|
2017-07-22 16:43:35 -04:00
|
|
|
|
text[6].key = const_cast<png_charp>("ember_time");
|
2014-12-07 02:51:44 -05:00
|
|
|
|
text[6].text = const_cast<png_charp>(comments.m_Runtime.c_str());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
text[7].compression = PNG_TEXT_COMPRESSION_zTXt;
|
2017-07-22 16:43:35 -04:00
|
|
|
|
text[7].key = const_cast<png_charp>("ember_genome");
|
2014-12-07 02:51:44 -05:00
|
|
|
|
text[7].text = const_cast<png_charp>(comments.m_Genome.c_str());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < height; i++)
|
2014-12-07 02:51:44 -05:00
|
|
|
|
rows[i] = image + i * width * 4 * bytesPerChannel;
|
2014-12-05 21:30:46 -05:00
|
|
|
|
|
|
|
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
|
|
|
|
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
|
|
|
{
|
|
|
|
|
fclose(file);
|
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
|
|
|
perror("writing file");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
png_init_io(png_ptr, file);
|
2014-12-07 02:51:44 -05:00
|
|
|
|
png_set_IHDR(png_ptr, info_ptr, png_uint_32(width), png_uint_32(height), 8 * png_uint_32(bytesPerChannel),
|
2016-03-01 20:26:45 -05:00
|
|
|
|
PNG_COLOR_TYPE_RGBA,
|
|
|
|
|
PNG_INTERLACE_NONE,
|
|
|
|
|
PNG_COMPRESSION_TYPE_BASE,
|
|
|
|
|
PNG_FILTER_TYPE_BASE);
|
2014-12-05 21:30:46 -05:00
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
if (enableComments == 1)
|
|
|
|
|
png_set_text(png_ptr, info_ptr, text, PNG_COMMENT_MAX);
|
|
|
|
|
|
|
|
|
|
png_write_info(png_ptr, info_ptr);
|
|
|
|
|
|
|
|
|
|
//Must set this after png_write_info().
|
|
|
|
|
if (bytesPerChannel == 2 && testbe != htons(testbe))
|
|
|
|
|
{
|
|
|
|
|
png_set_swap(png_ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-05 22:04:33 -05:00
|
|
|
|
png_write_image(png_ptr, rows.data());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
png_write_end(png_ptr, info_ptr);
|
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
|
|
|
fclose(file);
|
|
|
|
|
b = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Convert an RGB buffer to BGR for usage with BMP.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="buffer">The buffer to convert</param>
|
|
|
|
|
/// <param name="width">The width.</param>
|
|
|
|
|
/// <param name="height">The height.</param>
|
|
|
|
|
/// <param name="newSize">The size of the new buffer created</param>
|
|
|
|
|
/// <returns>The converted buffer if successful, else NULL.</returns>
|
2016-12-05 22:04:33 -05:00
|
|
|
|
static vector<byte> ConvertRGBToBMPBuffer(byte* buffer, size_t width, size_t height, size_t& newSize)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
2014-12-05 21:30:46 -05:00
|
|
|
|
if (buffer == nullptr || width == 0 || height == 0)
|
2016-12-05 22:04:33 -05:00
|
|
|
|
return vector<byte>();
|
2014-12-05 21:30:46 -05:00
|
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
|
size_t padding = 0;
|
|
|
|
|
size_t scanlinebytes = width * 3;
|
2016-03-01 20:26:45 -05:00
|
|
|
|
|
2014-12-05 21:30:46 -05:00
|
|
|
|
while ((scanlinebytes + padding ) % 4 != 0)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
padding++;
|
|
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
|
size_t psw = scanlinebytes + padding;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
newSize = height * psw;
|
2016-12-05 22:04:33 -05:00
|
|
|
|
vector<byte> newBuf(newSize);
|
|
|
|
|
size_t bufpos = 0;
|
|
|
|
|
size_t newpos = 0;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2016-12-05 22:04:33 -05:00
|
|
|
|
for (size_t y = 0; y < height; y++)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
2016-12-05 22:04:33 -05:00
|
|
|
|
for (size_t x = 0; x < 3 * width; x += 3)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
2016-12-05 22:04:33 -05:00
|
|
|
|
bufpos = y * 3 * width + x; // position in original buffer
|
|
|
|
|
newpos = (height - y - 1) * psw + x; // position in padded buffer
|
|
|
|
|
newBuf[newpos] = buffer[bufpos + 2]; // swap r and b
|
|
|
|
|
newBuf[newpos + 1] = buffer[bufpos + 1]; // g stays
|
|
|
|
|
newBuf[newpos + 2] = buffer[bufpos]; // swap b and r
|
|
|
|
|
//No swap.
|
|
|
|
|
//newBuf[newpos] = buffer[bufpos];
|
|
|
|
|
//newBuf[newpos + 1] = buffer[bufpos + 1];
|
|
|
|
|
//newBuf[newpos + 2] = buffer[bufpos + 2];
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-05 22:04:33 -05:00
|
|
|
|
return newBuf;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Save a Bmp file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="filename">The full path and name of the file</param>
|
|
|
|
|
/// <param name="image">Pointer to the image data to write</param>
|
|
|
|
|
/// <param name="width">Width of the image in pixels</param>
|
|
|
|
|
/// <param name="height">Height of the image in pixels</param>
|
|
|
|
|
/// <param name="paddedSize">Padded size, greater than or equal to total image size.</param>
|
|
|
|
|
/// <returns>True if success, else false</returns>
|
2016-12-05 22:04:33 -05:00
|
|
|
|
static bool SaveBmp(const char* filename, const byte* image, size_t width, size_t height, size_t paddedSize)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
2016-03-01 20:26:45 -05:00
|
|
|
|
#ifdef _WIN32
|
2014-07-08 03:11:14 -04:00
|
|
|
|
BITMAPFILEHEADER bmfh;
|
|
|
|
|
BITMAPINFOHEADER info;
|
2014-10-14 11:53:15 -04:00
|
|
|
|
DWORD bwritten;
|
2017-08-22 23:13:25 -04:00
|
|
|
|
HANDLE file = nullptr;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
memset (&bmfh, 0, sizeof (BITMAPFILEHEADER));
|
|
|
|
|
memset (&info, 0, sizeof (BITMAPINFOHEADER));
|
|
|
|
|
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
|
|
|
|
|
bmfh.bfReserved1 = 0;
|
|
|
|
|
bmfh.bfReserved2 = 0;
|
2014-10-14 11:53:15 -04:00
|
|
|
|
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (DWORD)paddedSize;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
bmfh.bfOffBits = 0x36;
|
|
|
|
|
info.biSize = sizeof(BITMAPINFOHEADER);
|
2014-10-14 11:53:15 -04:00
|
|
|
|
info.biWidth = (LONG)width;
|
|
|
|
|
info.biHeight = (LONG)height;
|
2014-12-05 21:30:46 -05:00
|
|
|
|
info.biPlanes = 1;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
info.biBitCount = 24;
|
2014-12-05 21:30:46 -05:00
|
|
|
|
info.biCompression = BI_RGB;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
info.biSizeImage = 0;
|
2014-12-05 21:30:46 -05:00
|
|
|
|
info.biXPelsPerMeter = 0x0ec4;
|
|
|
|
|
info.biYPelsPerMeter = 0x0ec4;
|
|
|
|
|
info.biClrUsed = 0;
|
|
|
|
|
info.biClrImportant = 0;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2017-08-22 23:13:25 -04:00
|
|
|
|
//Just to be extra safe.
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
rlg l(fileCs);
|
|
|
|
|
|
|
|
|
|
if ((file = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == NULL)
|
|
|
|
|
{
|
|
|
|
|
CloseHandle(file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
CloseHandle(file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (WriteFile(file, &bmfh, sizeof (BITMAPFILEHEADER), &bwritten, NULL) == false)
|
2014-12-05 21:30:46 -05:00
|
|
|
|
{
|
2014-07-08 03:11:14 -04:00
|
|
|
|
CloseHandle(file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false)
|
2014-12-05 21:30:46 -05:00
|
|
|
|
{
|
2014-07-08 03:11:14 -04:00
|
|
|
|
CloseHandle(file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
|
if (WriteFile(file, image, (DWORD)paddedSize, &bwritten, NULL) == false)
|
2014-12-05 21:30:46 -05:00
|
|
|
|
{
|
2014-07-08 03:11:14 -04:00
|
|
|
|
CloseHandle(file);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CloseHandle(file);
|
2014-12-05 21:30:46 -05:00
|
|
|
|
#endif
|
2014-07-08 03:11:14 -04:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Convert a buffer from RGB to BGR and write a Bmp file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="filename">The full path and name of the file</param>
|
|
|
|
|
/// <param name="image">Pointer to the image data to write</param>
|
|
|
|
|
/// <param name="width">Width of the image in pixels</param>
|
|
|
|
|
/// <param name="height">Height of the image in pixels</param>
|
|
|
|
|
/// <returns>True if success, else false</returns>
|
2014-12-06 00:05:09 -05:00
|
|
|
|
static bool WriteBmp(const char* filename, byte* image, size_t width, size_t height)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
bool b = false;
|
2014-10-14 11:53:15 -04:00
|
|
|
|
size_t newSize;
|
2016-12-05 22:04:33 -05:00
|
|
|
|
auto bgrBuf = ConvertRGBToBMPBuffer(image, width, height, newSize);
|
|
|
|
|
b = SaveBmp(filename, bgrBuf.data(), width, height, newSize);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
return b;
|
|
|
|
|
}
|
2017-07-22 16:43:35 -04:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Write an EXR file.
|
|
|
|
|
/// This is used for extreme color precision because it uses
|
|
|
|
|
/// floats for each color channel.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="filename">The full path and name of the file</param>
|
|
|
|
|
/// <param name="image">Pointer to the image data to write</param>
|
|
|
|
|
/// <param name="width">Width of the image in pixels</param>
|
|
|
|
|
/// <param name="height">Height of the image in pixels</param>
|
|
|
|
|
/// <param name="enableComments">True to embed comments, else false</param>
|
|
|
|
|
/// <param name="comments">The comment string to embed</param>
|
|
|
|
|
/// <param name="id">Id of the author</param>
|
|
|
|
|
/// <param name="url">Url of the author</param>
|
|
|
|
|
/// <param name="nick">Nickname of the author</param>
|
|
|
|
|
/// <returns>True if success, else false</returns>
|
|
|
|
|
static bool WriteExr(const char* filename, Rgba* image, size_t width, size_t height, bool enableComments, const EmberImageComments& comments, const string& id, const string& url, const string& nick)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
int iw = int(width);
|
|
|
|
|
int ih = int(height);
|
2017-08-22 23:13:25 -04:00
|
|
|
|
std::unique_ptr<RgbaOutputFile> file;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
rlg l(fileCs);
|
|
|
|
|
file = std::make_unique<RgbaOutputFile>(filename, iw, ih, WRITE_RGBA);
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-07-22 16:43:35 -04:00
|
|
|
|
|
|
|
|
|
if (enableComments)
|
|
|
|
|
{
|
2017-08-22 23:13:25 -04:00
|
|
|
|
auto& header = const_cast<Imf::Header&>(file->header());
|
2017-07-22 16:43:35 -04:00
|
|
|
|
header.insert("ember_version", StringAttribute(EmberVersion()));
|
|
|
|
|
header.insert("ember_nickname", StringAttribute(nick));
|
|
|
|
|
header.insert("ember_url", StringAttribute(url));
|
|
|
|
|
header.insert("ember_id", StringAttribute(id));
|
|
|
|
|
header.insert("ember_error_rate", StringAttribute(comments.m_Badvals));
|
|
|
|
|
header.insert("ember_samples", StringAttribute(comments.m_NumIters));
|
|
|
|
|
header.insert("ember_time", StringAttribute(comments.m_Runtime));
|
|
|
|
|
header.insert("ember_genome", StringAttribute(comments.m_Genome));
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-22 23:13:25 -04:00
|
|
|
|
file->setFrameBuffer(image, 1, iw);
|
|
|
|
|
file->writePixels(ih);
|
2017-07-22 16:43:35 -04:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (std::exception e)
|
|
|
|
|
{
|
|
|
|
|
cout << e.what() << endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|