Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| /** | |
| * Mandelbulber v2, a 3D fractal generator ,=#MKNmMMKmmßMNWy, | |
| * ,B" ]L,,p%%%,,,§;, "K | |
| * Copyright (C) 2016-18 Mandelbulber Team §R-==%w["'~5]m%=L.=~5N | |
| * ,=mm=§M ]=4 yJKA"/-Nsaj "Bw,==,, | |
| * This file is part of Mandelbulber. §R.r= jw",M Km .mM FW ",§=ß., ,TN | |
| * ,4R =%["w[N=7]J '"5=],""]]M,w,-; T=]M | |
| * Mandelbulber is free software: §R.ß~-Q/M=,=5"v"]=Qf,'§"M= =,M.§ Rz]M"Kw | |
| * you can redistribute it and/or §w "xDY.J ' -"m=====WeC=\ ""%""y=%"]"" § | |
| * modify it under the terms of the "§M=M =D=4"N #"%==A%p M§ M6 R' #"=~.4M | |
| * GNU General Public License as §W =, ][T"]C § § '§ e===~ U !§[Z ]N | |
| * published by the 4M",,Jm=,"=e~ § § j]]""N BmM"py=ßM | |
| * Free Software Foundation, ]§ T,M=& 'YmMMpM9MMM%=w=,,=MT]M m§;'§, | |
| * either version 3 of the License, TWw [.j"5=~N[=§%=%W,T ]R,"=="Y[LFT ]N | |
| * or (at your option) TW=,-#"%=;[ =Q:["V"" ],,M.m == ]N | |
| * any later version. J§"mr"] ,=,," =="""J]= M"M"]==ß" | |
| * §= "=C=4 §"eM "=B:m|4"]#F,§~ | |
| * Mandelbulber is distributed in "9w=,,]w em%wJ '"~" ,=,,ß" | |
| * the hope that it will be useful, . "K= ,=RMMMßM""" | |
| * but WITHOUT ANY WARRANTY; .''' | |
| * without even the implied warranty | |
| * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| * | |
| * See the GNU General Public License for more details. | |
| * You should have received a copy of the GNU General Public License | |
| * along with Mandelbulber. If not, see <http://www.gnu.org/licenses/>. | |
| * | |
| * ########################################################################### | |
| * | |
| * Authors: Sebastian Jennen (jenzebas@gmail.com) | |
| * | |
| * file image class to store different image file formats | |
| * | |
| * Each image file type derives ImageFileSave and implements the SaveImage | |
| * method to store the image data with the corresponding file format | |
| */ | |
| #include "file_image.hpp" | |
| #include "cimage.hpp" | |
| #include "error_message.hpp" | |
| #include "files.h" | |
| #include "initparameters.hpp" | |
| #include "parameters.hpp" | |
| // custom includes | |
| #ifdef USE_TIFF | |
| #include "tiff.h" | |
| #include "tiffio.h" | |
| #endif // USE_TIFF | |
| #ifdef USE_EXR | |
| #include <ImfAttribute.h> | |
| #include <ImfChannelList.h> | |
| #include <ImfFrameBuffer.h> | |
| #include <ImfInputFile.h> | |
| #include <ImfOutputFile.h> | |
| #include <half.h> | |
| #endif // USE_EXR | |
| #define PNG_DEBUG 3 | |
| const uint64_t ImageFileSave::SAVE_CHUNK_SIZE; | |
| ImageFileSave::ImageFileSave(QString filename, cImage *image, ImageConfig imageConfig) | |
| { | |
| this->filename = filename; | |
| this->image = image; | |
| this->imageConfig = imageConfig; | |
| currentChannel = 0; | |
| totalChannel = 0; | |
| currentChannelKey = IMAGE_CONTENT_COLOR; | |
| } | |
| ImageFileSave *ImageFileSave::create( | |
| QString filename, enumImageFileType fileType, cImage *image, ImageConfig imageConfig) | |
| { | |
| switch (fileType) | |
| { | |
| case IMAGE_FILE_TYPE_PNG: return new ImageFileSavePNG(filename, image, imageConfig); | |
| case IMAGE_FILE_TYPE_JPG: return new ImageFileSaveJPG(filename, image, imageConfig); | |
| #ifdef USE_TIFF | |
| case IMAGE_FILE_TYPE_TIFF: return new ImageFileSaveTIFF(filename, image, imageConfig); | |
| #endif /* USE_TIFF */ | |
| #ifdef USE_EXR | |
| case IMAGE_FILE_TYPE_EXR: return new ImageFileSaveEXR(filename, image, imageConfig); | |
| #endif /* USE_EXR */ | |
| } | |
| qCritical() << "fileType " << ImageFileExtension(fileType) << " not supported!"; | |
| return nullptr; | |
| } | |
| QString ImageFileSave::ImageFileExtension(enumImageFileType imageFileType) | |
| { | |
| switch (imageFileType) | |
| { | |
| case IMAGE_FILE_TYPE_JPG: return "jpg"; | |
| case IMAGE_FILE_TYPE_PNG: return "png"; | |
| case IMAGE_FILE_TYPE_EXR: return "exr"; | |
| case IMAGE_FILE_TYPE_TIFF: return "tiff"; | |
| } | |
| return ""; | |
| } | |
| QString ImageFileSave::ImageChannelName(enumImageContentType imageContentType) | |
| { | |
| switch (imageContentType) | |
| { | |
| case IMAGE_CONTENT_COLOR: return "color"; | |
| case IMAGE_CONTENT_ALPHA: return "alpha"; | |
| case IMAGE_CONTENT_ZBUFFER: return "zbuffer"; | |
| case IMAGE_CONTENT_NORMAL: return "normal"; | |
| } | |
| return ""; | |
| } | |
| QStringList ImageFileSave::ImageChannelNames() | |
| { | |
| return QStringList({"color", "alpha", "zbuffer", "normal"}); | |
| } | |
| ImageFileSave::enumImageFileType ImageFileSave::ImageFileType(QString imageFileExtension) | |
| { | |
| if (imageFileExtension == "jpg" || imageFileExtension == "jpeg") | |
| return IMAGE_FILE_TYPE_JPG; | |
| else if (imageFileExtension == "png") | |
| return IMAGE_FILE_TYPE_PNG; | |
| else if (imageFileExtension == "exr") | |
| return IMAGE_FILE_TYPE_EXR; | |
| else if (imageFileExtension == "tiff" || imageFileExtension == "tif") | |
| return IMAGE_FILE_TYPE_TIFF; | |
| else | |
| return IMAGE_FILE_TYPE_JPG; | |
| } | |
| QString ImageFileSave::ImageNameWithoutExtension(QString path) | |
| { | |
| QFileInfo fi(path); | |
| QString fileName = fi.completeBaseName(); | |
| if (fi.suffix() != "" | |
| && !QStringList({"jpg", "jpeg", "png", "exr", "tiff", "tif"}).contains(fi.suffix())) | |
| { | |
| fileName += "." + fi.suffix(); | |
| } | |
| return fi.path() + QDir::separator() + fileName; | |
| } | |
| void ImageFileSave::updateProgressAndStatusChannel(double progress) | |
| { | |
| emit updateProgressAndStatus(getJobName(), | |
| QObject::tr("Saving channel: %1").arg(ImageChannelName(currentChannelKey)), | |
| (1.0 * currentChannel / totalChannel) + (progress / totalChannel)); | |
| } | |
| void ImageFileSave::updateProgressAndStatusStarted() | |
| { | |
| emit updateProgressAndStatus(getJobName(), QObject::tr("Started"), 0.0); | |
| } | |
| void ImageFileSave::updateProgressAndStatusFinished() | |
| { | |
| emit updateProgressAndStatus(getJobName(), QObject::tr("Finished"), 1.0); | |
| } | |
| void ImageFileSavePNG::SaveImage() | |
| { | |
| emit updateProgressAndStatusStarted(); | |
| bool appendAlpha = gPar->Get<bool>("append_alpha_png") | |
| && imageConfig.contains(IMAGE_CONTENT_COLOR) | |
| && imageConfig.contains(IMAGE_CONTENT_ALPHA); | |
| if (hasAppendAlphaCustom) appendAlpha = appendAlphaCustom; | |
| currentChannel = 0; | |
| totalChannel = imageConfig.size(); | |
| for (ImageConfig::iterator channel = imageConfig.begin(); channel != imageConfig.end(); ++channel) | |
| { | |
| currentChannelKey = channel.key(); | |
| QString fullFilename = filename + channel.value().postfix + ".png"; | |
| emit updateProgressAndStatus(getJobName(), | |
| QObject::tr("Saving channel: %1").arg(ImageChannelName(currentChannelKey)), | |
| 1.0 * currentChannel / totalChannel); | |
| switch (currentChannelKey) | |
| { | |
| case IMAGE_CONTENT_COLOR: SavePNG(fullFilename, image, channel.value(), appendAlpha); break; | |
| case IMAGE_CONTENT_ALPHA: | |
| if (!appendAlpha) SavePNG(fullFilename, image, channel.value()); | |
| break; | |
| case IMAGE_CONTENT_ZBUFFER: | |
| case IMAGE_CONTENT_NORMAL: | |
| default: SavePNG(fullFilename, image, channel.value()); break; | |
| } | |
| currentChannel++; | |
| } | |
| emit updateProgressAndStatusFinished(); | |
| } | |
| void ImageFileSaveJPG::SaveImage() | |
| { | |
| emit updateProgressAndStatusStarted(); | |
| currentChannel = 0; | |
| totalChannel = imageConfig.size(); | |
| for (ImageConfig::iterator channel = imageConfig.begin(); channel != imageConfig.end(); ++channel) | |
| { | |
| currentChannelKey = channel.key(); | |
| QString fullFilename = filename + channel.value().postfix + ".jpg"; | |
| emit updateProgressAndStatus(getJobName(), | |
| QObject::tr("Saving channel: %1").arg(ImageChannelName(currentChannelKey)), | |
| 1.0 * currentChannel / totalChannel); | |
| switch (currentChannelKey) | |
| { | |
| case IMAGE_CONTENT_COLOR: | |
| SaveJPEGQt(fullFilename, image->ConvertTo8bit(), image->GetWidth(), image->GetHeight(), | |
| gPar->Get<int>("jpeg_quality")); | |
| break; | |
| case IMAGE_CONTENT_ALPHA: | |
| SaveJPEGQtGreyscale(fullFilename, image->ConvertAlphaTo8bit(), image->GetWidth(), | |
| image->GetHeight(), gPar->Get<int>("jpeg_quality")); | |
| break; | |
| case IMAGE_CONTENT_ZBUFFER: | |
| qWarning() << "JPG cannot save zbuffer (loss of precision to strong)"; | |
| break; | |
| case IMAGE_CONTENT_NORMAL: | |
| SaveJPEGQt(fullFilename, image->ConvertNormalTo8Bit(), image->GetWidth(), | |
| image->GetHeight(), gPar->Get<int>("jpeg_quality")); | |
| break; | |
| default: qWarning() << "Unknown channel for JPG"; break; | |
| } | |
| currentChannel++; | |
| } | |
| emit updateProgressAndStatusFinished(); | |
| } | |
| #ifdef USE_TIFF | |
| void ImageFileSaveTIFF::SaveImage() | |
| { | |
| emit updateProgressAndStatusStarted(); | |
| bool appendAlpha = gPar->Get<bool>("append_alpha_png") | |
| && imageConfig.contains(IMAGE_CONTENT_COLOR) | |
| && imageConfig.contains(IMAGE_CONTENT_ALPHA); | |
| currentChannel = 0; | |
| totalChannel = imageConfig.size(); | |
| for (ImageConfig::iterator channel = imageConfig.begin(); channel != imageConfig.end(); ++channel) | |
| { | |
| currentChannelKey = channel.key(); | |
| QString fullFilename = filename + channel.value().postfix + ".tiff"; | |
| emit updateProgressAndStatus(getJobName(), | |
| QObject::tr("Saving channel: %1").arg(ImageChannelName(currentChannelKey)), | |
| 1.0 * currentChannel / totalChannel); | |
| switch (currentChannelKey) | |
| { | |
| case IMAGE_CONTENT_COLOR: SaveTIFF(fullFilename, image, channel.value(), appendAlpha); break; | |
| case IMAGE_CONTENT_ALPHA: | |
| if (!appendAlpha) SaveTIFF(fullFilename, image, channel.value()); | |
| break; | |
| case IMAGE_CONTENT_ZBUFFER: | |
| case IMAGE_CONTENT_NORMAL: | |
| default: SaveTIFF(fullFilename, image, channel.value()); break; | |
| } | |
| currentChannel++; | |
| } | |
| emit updateProgressAndStatusFinished(); | |
| } | |
| #endif /* USE_TIFF */ | |
| #ifdef USE_EXR | |
| void ImageFileSaveEXR::SaveImage() | |
| { | |
| emit updateProgressAndStatusStarted(); | |
| QString fullFilename = filename + ".exr"; | |
| SaveEXR(fullFilename, image, imageConfig); | |
| emit updateProgressAndStatusFinished(); | |
| } | |
| #endif /* USE_EXR */ | |
| void ImageFileSavePNG::SavePNG( | |
| QString filenameInput, cImage *image, structSaveImageChannel imageChannel, bool appendAlpha) | |
| { | |
| uint64_t width = image->GetWidth(); | |
| uint64_t height = image->GetHeight(); | |
| /* create file */ | |
| FILE *fp = fopen(filenameInput.toLocal8Bit().constData(), "wb"); | |
| png_bytep *row_pointers = nullptr; | |
| png_structp png_ptr = nullptr; | |
| png_info *info_ptr = nullptr; | |
| char *colorPtr = nullptr; | |
| try | |
| { | |
| if (!fp) throw QString("[write_png_file] File %s could not be opened for writing."); | |
| /* initialize stuff */ | |
| png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); | |
| if (!png_ptr) throw QString("[write_png_file] png_create_write_struct failed"); | |
| info_ptr = png_create_info_struct(png_ptr); | |
| if (!info_ptr) throw QString("[write_png_file] png_create_info_struct failed"); | |
| if (setjmp(png_jmpbuf(png_ptr))) throw QString("[write_png_file] Error during init_io"); | |
| png_init_io(png_ptr, fp); | |
| /* write header */ | |
| if (setjmp(png_jmpbuf(png_ptr))) throw QString("[write_png_file] Error during writing header"); | |
| if (imageChannel.channelQuality != IMAGE_CHANNEL_QUALITY_8 | |
| && imageChannel.channelQuality != IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| // for PNG no more than 16 bit per channel possible | |
| imageChannel.channelQuality = IMAGE_CHANNEL_QUALITY_16; | |
| } | |
| int qualitySize; | |
| switch (imageChannel.channelQuality) | |
| { | |
| case IMAGE_CHANNEL_QUALITY_8: qualitySize = 8; break; | |
| case IMAGE_CHANNEL_QUALITY_16: qualitySize = 16; break; | |
| default: qualitySize = 8; break; | |
| } | |
| int qualitySizeByte = qualitySize / 8; | |
| int colorType; | |
| switch (imageChannel.contentType) | |
| { | |
| case IMAGE_CONTENT_COLOR: | |
| colorType = appendAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; | |
| break; | |
| case IMAGE_CONTENT_ALPHA: | |
| case IMAGE_CONTENT_ZBUFFER: colorType = PNG_COLOR_TYPE_GRAY; break; | |
| case IMAGE_CONTENT_NORMAL: colorType = PNG_COLOR_TYPE_RGB; break; | |
| default: colorType = PNG_COLOR_TYPE_RGB; break; | |
| } | |
| png_set_IHDR(png_ptr, info_ptr, width, height, qualitySize, colorType, PNG_INTERLACE_NONE, | |
| PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
| png_write_info(png_ptr, info_ptr); | |
| png_set_swap(png_ptr); | |
| /* write bytes */ | |
| if (setjmp(png_jmpbuf(png_ptr))) throw QString("[write_png_file] Error during writing bytes"); | |
| row_pointers = new png_bytep[height]; | |
| uint64_t pixelSize = qualitySizeByte; | |
| switch (imageChannel.contentType) | |
| { | |
| case IMAGE_CONTENT_COLOR: pixelSize *= appendAlpha ? 4 : 3; break; | |
| case IMAGE_CONTENT_ALPHA: pixelSize *= 1; break; | |
| case IMAGE_CONTENT_ZBUFFER: pixelSize *= 1; break; | |
| case IMAGE_CONTENT_NORMAL: pixelSize *= 3; break; | |
| } | |
| bool directOnBuffer = false; | |
| if (imageChannel.contentType == IMAGE_CONTENT_COLOR && !appendAlpha) directOnBuffer = true; | |
| if (imageChannel.contentType == IMAGE_CONTENT_ALPHA) directOnBuffer = true; | |
| if (directOnBuffer) | |
| { | |
| char *directPointer = nullptr; | |
| switch (imageChannel.contentType) | |
| { | |
| case IMAGE_CONTENT_COLOR: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| directPointer = reinterpret_cast<char *>(image->GetImage16Ptr()); | |
| } | |
| else | |
| { | |
| directPointer = reinterpret_cast<char *>(image->ConvertTo8bit()); | |
| } | |
| } | |
| break; | |
| case IMAGE_CONTENT_ALPHA: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| directPointer = reinterpret_cast<char *>(image->GetAlphaBufPtr()); | |
| } | |
| else | |
| { | |
| directPointer = reinterpret_cast<char *>(image->ConvertAlphaTo8bit()); | |
| } | |
| } | |
| break; | |
| case IMAGE_CONTENT_ZBUFFER: | |
| case IMAGE_CONTENT_NORMAL: | |
| // zbuffer and normals are float, so direct buffer write is not applicable | |
| break; | |
| } | |
| for (uint64_t y = 0; y < height; y++) | |
| { | |
| row_pointers[y] = reinterpret_cast<png_byte *>(&directPointer[y * width * pixelSize]); | |
| } | |
| } | |
| else | |
| { | |
| colorPtr = new char[uint64_t(width) * height * pixelSize]; | |
| // calculate min / max values from zbuffer range | |
| float minZ = float(1.0e50); | |
| float maxZ = 0.0; | |
| if (imageChannel.contentType == IMAGE_CONTENT_ZBUFFER) | |
| { | |
| float *zbuffer = image->GetZBufferPtr(); | |
| uint64_t size = width * height; | |
| for (uint64_t i = 0; i < size; i++) | |
| { | |
| float z = zbuffer[i]; | |
| if (z > maxZ && z < 1e19) maxZ = z; | |
| if (z < minZ) minZ = z; | |
| } | |
| } | |
| double kZ = log(maxZ / minZ); | |
| for (uint64_t y = 0; y < height; y++) | |
| { | |
| for (uint64_t x = 0; x < width; x++) | |
| { | |
| uint64_t ptr = (x + y * width) * pixelSize; | |
| switch (imageChannel.contentType) | |
| { | |
| case IMAGE_CONTENT_COLOR: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| if (appendAlpha) | |
| { | |
| sRGBA16 *typedColorPtr = reinterpret_cast<sRGBA16 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGBA16(image->GetPixelImage16(x, y)); | |
| typedColorPtr->A = image->GetPixelAlpha(x, y); | |
| } | |
| } | |
| else | |
| { | |
| if (appendAlpha) | |
| { | |
| if (x == 0 && y == 0) | |
| { | |
| image->ConvertAlphaTo8bit(); | |
| image->ConvertTo8bit(); | |
| } | |
| sRGBA8 *typedColorPtr = reinterpret_cast<sRGBA8 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGBA8(image->GetPixelImage8(x, y)); | |
| typedColorPtr->A = image->GetPixelAlpha8(x, y); | |
| } | |
| } | |
| } | |
| break; | |
| case IMAGE_CONTENT_ALPHA: | |
| // all alpha savings to PNG happen directly on buffers in cimage | |
| break; | |
| case IMAGE_CONTENT_ZBUFFER: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| float z = image->GetPixelZBuffer(x, y); | |
| float z1 = log(z / minZ) / kZ; | |
| int intZ = z1 * 60000; | |
| if (z > 1e19) intZ = 65535; | |
| unsigned short *typedColorPtr = reinterpret_cast<unsigned short *>(&colorPtr[ptr]); | |
| *typedColorPtr = static_cast<unsigned short>(intZ); | |
| } | |
| else | |
| { | |
| float z = image->GetPixelZBuffer(x, y); | |
| float z1 = log(z / minZ) / kZ; | |
| int intZ = z1 * 240; | |
| if (z > 1e19) intZ = 255; | |
| unsigned char *typedColorPtr = reinterpret_cast<unsigned char *>(&colorPtr[ptr]); | |
| *typedColorPtr = static_cast<unsigned char>(intZ); | |
| } | |
| } | |
| break; | |
| case IMAGE_CONTENT_NORMAL: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| if (x == 0 && y == 0) image->ConvertNormalTo16Bit(); | |
| sRGB16 *typedColorPtr = reinterpret_cast<sRGB16 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGB16(image->GetPixelNormal16(x, y)); | |
| } | |
| else | |
| { | |
| if (x == 0 && y == 0) image->ConvertNormalTo8Bit(); | |
| sRGB8 *typedColorPtr = reinterpret_cast<sRGB8 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGB8(image->GetPixelNormal8(x, y)); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| row_pointers[y] = reinterpret_cast<png_byte *>(&colorPtr[y * width * pixelSize]); | |
| } | |
| } | |
| // png_write_image(png_ptr, row_pointers); | |
| for (uint64_t r = 0; r < height; r += SAVE_CHUNK_SIZE) | |
| { | |
| uint64_t currentChunkSize = min(height - r, SAVE_CHUNK_SIZE); | |
| png_write_rows(png_ptr, png_bytepp(&row_pointers[r]), currentChunkSize); | |
| emit updateProgressAndStatusChannel(1.0 * r / height); | |
| } | |
| /* end write */ | |
| if (setjmp(png_jmpbuf(png_ptr))) throw QString("[write_png_file] Error during end of write"); | |
| png_write_end(png_ptr, info_ptr); | |
| png_destroy_write_struct(&png_ptr, &info_ptr); | |
| delete[] row_pointers; | |
| if (colorPtr) delete[] colorPtr; | |
| fclose(fp); | |
| } | |
| catch (QString &status) | |
| { | |
| if (png_ptr) | |
| { | |
| if (info_ptr) | |
| { | |
| png_destroy_write_struct(&png_ptr, &info_ptr); | |
| } | |
| else | |
| { | |
| png_destroy_write_struct(&png_ptr, nullptr); | |
| } | |
| } | |
| if (row_pointers) delete[] row_pointers; | |
| if (colorPtr) delete[] colorPtr; | |
| if (fp) fclose(fp); | |
| cErrorMessage::showMessage( | |
| QObject::tr("Can't save image to PNG file!\n") + filenameInput + "\n" + status, | |
| cErrorMessage::errorMessage); | |
| } | |
| } | |
| void ImageFileSavePNG::SavePNG16(QString filename, int width, int height, sRGB16 *image16) | |
| { | |
| /* create file */ | |
| FILE *fp = fopen(filename.toLocal8Bit().constData(), "wb"); | |
| png_bytep *row_pointers = nullptr; | |
| png_structp png_ptr = nullptr; | |
| png_info *info_ptr = nullptr; | |
| try | |
| { | |
| if (!fp) throw QString("[write_png_file] File %s could not be opened for writing."); | |
| /* initialize stuff */ | |
| png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); | |
| if (!png_ptr) throw QString("[write_png_file] png_create_write_struct failed"); | |
| info_ptr = png_create_info_struct(png_ptr); | |
| if (!info_ptr) throw QString("[write_png_file] png_create_info_struct failed"); | |
| if (setjmp(png_jmpbuf(png_ptr))) throw QString("[write_png_file] Error during init_io"); | |
| png_init_io(png_ptr, fp); | |
| /* write header */ | |
| if (setjmp(png_jmpbuf(png_ptr))) throw QString("[write_png_file] Error during writing header"); | |
| png_set_IHDR(png_ptr, info_ptr, width, height, 16, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, | |
| PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
| png_write_info(png_ptr, info_ptr); | |
| png_set_swap(png_ptr); | |
| /* write bytes */ | |
| if (setjmp(png_jmpbuf(png_ptr))) throw QString("[write_png_file] Error during writing bytes"); | |
| row_pointers = new png_bytep[height]; | |
| for (int y = 0; y < height; y++) | |
| { | |
| row_pointers[y] = reinterpret_cast<png_byte *>(&image16[y * width]); | |
| } | |
| png_write_image(png_ptr, row_pointers); | |
| /* end write */ | |
| if (setjmp(png_jmpbuf(png_ptr))) throw QString("[write_png_file] Error during end of write"); | |
| png_write_end(png_ptr, info_ptr); | |
| png_destroy_write_struct(&png_ptr, &info_ptr); | |
| delete[] row_pointers; | |
| fclose(fp); | |
| } | |
| catch (QString &status) | |
| { | |
| if (png_ptr) | |
| { | |
| if (info_ptr) | |
| { | |
| png_destroy_write_struct(&png_ptr, &info_ptr); | |
| } | |
| else | |
| { | |
| png_destroy_write_struct(&png_ptr, nullptr); | |
| } | |
| } | |
| if (row_pointers) delete[] row_pointers; | |
| if (fp) fclose(fp); | |
| cErrorMessage::showMessage( | |
| QObject::tr("Can't save image to PNG file!\n") + filename + "\n" + status, | |
| cErrorMessage::errorMessage); | |
| } | |
| } | |
| void ImageFileSavePNG::SaveFromTilesPNG16(const char *filename, int width, int height, int tiles) | |
| { | |
| /* create file */ | |
| string filenamePNG(filename); | |
| filenamePNG += "_fromTiles.png"; | |
| FILE *fp = fopen(filenamePNG.c_str(), "wb"); | |
| if (!fp) | |
| { | |
| fprintf(stderr, "[write_png_file] File %s could not be opened for writing", filename); | |
| return; | |
| } | |
| /* initialize stuff */ | |
| png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); | |
| if (!png_ptr) | |
| { | |
| fprintf(stderr, "[write_png_file] png_create_write_struct failed"); | |
| fclose(fp); | |
| return; | |
| } | |
| png_info *info_ptr = png_create_info_struct(png_ptr); | |
| if (!info_ptr) | |
| { | |
| fprintf(stderr, "[write_png_file] png_create_info_struct failed"); | |
| fclose(fp); | |
| return; | |
| } | |
| if (setjmp(png_jmpbuf(png_ptr))) | |
| { | |
| fprintf(stderr, "[write_png_file] Error during init_io"); | |
| fclose(fp); | |
| return; | |
| } | |
| png_init_io(png_ptr, fp); | |
| /* write header */ | |
| if (setjmp(png_jmpbuf(png_ptr))) | |
| { | |
| fprintf(stderr, "[write_png_file] Error during writing header"); | |
| fclose(fp); | |
| return; | |
| } | |
| png_set_IHDR(png_ptr, info_ptr, width * tiles, height * tiles, 16, PNG_COLOR_TYPE_RGB, | |
| PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
| png_write_info(png_ptr, info_ptr); | |
| png_set_swap(png_ptr); | |
| /* write bytes */ | |
| if (setjmp(png_jmpbuf(png_ptr))) | |
| { | |
| fprintf(stderr, "[write_png_file] Error during writing bytes"); | |
| fclose(fp); | |
| return; | |
| } | |
| FILE **files = new FILE *[tiles]; | |
| sRGB16 *rowBuffer = new sRGB16[width * tiles]; | |
| for (int tileRow = 0; tileRow < tiles; tileRow++) | |
| { | |
| printf("Compiling image from tiles, row %d\n", tileRow); | |
| for (int tile = 0; tile < tiles; tile++) | |
| { | |
| int fileNumber = tile + tileRow * tiles; | |
| string filename2 = IndexFilename(filename, "tile", fileNumber); | |
| files[tile] = fopen(filename2.c_str(), "rb"); | |
| } | |
| for (int y = 0; y < height; y++) | |
| { | |
| for (int tile = 0; tile < tiles; tile++) | |
| { | |
| size_t result = fread(&rowBuffer[tile * width], 1, sizeof(sRGB16) * width, files[tile]); | |
| if (result != sizeof(sRGB16) * width) | |
| { | |
| printf("Reading error of image tile files"); | |
| for (int tile2 = 0; tile2 < tiles; tile2++) | |
| fclose(files[tile2]); | |
| delete[] rowBuffer; | |
| delete[] files; | |
| return; | |
| } | |
| } | |
| png_write_rows(png_ptr, reinterpret_cast<png_bytep *>(&rowBuffer), 1); | |
| } | |
| for (int tile = 0; tile < tiles; tile++) | |
| { | |
| fclose(files[tile]); | |
| int fileNumber = tile + tileRow * tiles; | |
| string filename2 = IndexFilename(filename, "tile", fileNumber); | |
| remove(filename2.c_str()); | |
| } | |
| } | |
| /* end write */ | |
| if (setjmp(png_jmpbuf(png_ptr))) | |
| { | |
| fprintf(stderr, "[write_png_file] Error during end of write"); | |
| delete[] rowBuffer; | |
| delete[] files; | |
| fclose(fp); | |
| return; | |
| } | |
| png_write_end(png_ptr, info_ptr); | |
| png_destroy_write_struct(&png_ptr, &info_ptr); | |
| delete[] rowBuffer; | |
| delete[] files; | |
| fclose(fp); | |
| } | |
| bool ImageFileSaveJPG::SaveJPEGQt( | |
| QString filename, unsigned char *image, int width, int height, int quality) | |
| { | |
| if (!image) | |
| { | |
| WriteLog( | |
| "ImageFileSaveJPG::SaveJPEGQt(): " | |
| "the image is a null pointer, this might be the case for optional channel(s). " | |
| "If this is the case, just rerender the image with enabled channel(s).", | |
| 1); | |
| return false; | |
| } | |
| QImage *qImage = new QImage(width, height, QImage::Format_RGB888); | |
| for (int line = 0; line < height; line++) | |
| { | |
| unsigned char *linePointer = &image[line * width * 3]; | |
| unsigned char *qScanLine = qImage->scanLine(line); | |
| memcpy(qScanLine, linePointer, sizeof(unsigned char) * 3 * width); | |
| } | |
| QFile file(filename); | |
| file.open(QIODevice::WriteOnly); | |
| bool result = qImage->save(&file, "JPEG", quality); | |
| if (!result) | |
| { | |
| cErrorMessage::showMessage( | |
| QObject::tr("Can't save image to JPEG file!\n") + filename + "\n" + file.errorString(), | |
| cErrorMessage::errorMessage); | |
| } | |
| file.close(); | |
| delete qImage; | |
| return result; | |
| } | |
| bool ImageFileSaveJPG::SaveJPEGQtGreyscale( | |
| QString filename, unsigned char *image, int width, int height, int quality) | |
| { | |
| if (!image) | |
| { | |
| WriteLog( | |
| "ImageFileSaveJPG::SaveJPEGQtGreyscale(): " | |
| "the image is a null pointer, this might be the case for optional channel(s). " | |
| "If this is the case, just rerender the image with enabled channel(s).", | |
| 1); | |
| return false; | |
| } | |
| QImage *qImage = new QImage(width, height, QImage::Format_Indexed8); | |
| QVector<QRgb> my_table; | |
| for (int i = 0; i < 256; i++) | |
| my_table.push_back(qRgb(i, i, i)); | |
| qImage->setColorTable(my_table); | |
| for (int line = 0; line < height; line++) | |
| { | |
| unsigned char *linePointer = &image[line * width]; | |
| unsigned char *qScanLine = qImage->scanLine(line); | |
| memcpy(qScanLine, linePointer, sizeof(unsigned char) * width); | |
| } | |
| QFile file(filename); | |
| file.open(QIODevice::WriteOnly); | |
| bool result = qImage->save(&file, "JPEG", quality); | |
| if (!result) | |
| { | |
| cErrorMessage::showMessage( | |
| QObject::tr("Can't save image to JPEG file!\n") + filename + "\n" + file.errorString(), | |
| cErrorMessage::errorMessage); | |
| } | |
| file.close(); | |
| delete qImage; | |
| return result; | |
| } | |
| bool ImageFileSavePNG::SavePNGQtBlackAndWhite( | |
| QString filename, unsigned char *image, int width, int height) | |
| { | |
| QImage *qImage = new QImage(width, height, QImage::Format_Mono); | |
| QVector<QRgb> my_table; | |
| my_table.push_back(qRgb(0, 0, 0)); | |
| my_table.push_back(qRgb(255, 255, 255)); | |
| qImage->setColorTable(my_table); | |
| for (int i = 0; i < width; i++) | |
| { | |
| for (int j = 0; j < height; j++) | |
| { | |
| qImage->setPixel(i, j, image[i + j * width]); | |
| } | |
| } | |
| QFile file(filename); | |
| file.open(QIODevice::WriteOnly); | |
| bool result = qImage->save(&file, "PNG"); | |
| if (!result) | |
| { | |
| cErrorMessage::showMessage( | |
| QObject::tr("Can't save image to JPEG file!\n") + filename + "\n" + file.errorString(), | |
| cErrorMessage::errorMessage); | |
| } | |
| file.close(); | |
| delete qImage; | |
| return result; | |
| } | |
| #ifdef USE_EXR | |
| void ImageFileSaveEXR::SaveEXR( | |
| QString filename, cImage *image, QMap<enumImageContentType, structSaveImageChannel> imageConfig) | |
| { | |
| uint64_t width = image->GetWidth(); | |
| uint64_t height = image->GetHeight(); | |
| Imf::Header header(width, height); | |
| Imf::FrameBuffer frameBuffer; | |
| header.compression() = Imf::ZIP_COMPRESSION; | |
| if (imageConfig.contains(IMAGE_CONTENT_COLOR)) | |
| { | |
| // add rgb channel header | |
| Imf::PixelType imfQuality = | |
| imageConfig[IMAGE_CONTENT_COLOR].channelQuality == IMAGE_CHANNEL_QUALITY_32 ? Imf::FLOAT | |
| : Imf::HALF; | |
| header.channels().insert("R", Imf::Channel(imfQuality)); | |
| header.channels().insert("G", Imf::Channel(imfQuality)); | |
| header.channels().insert("B", Imf::Channel(imfQuality)); | |
| int pixelSize = sizeof(tsRGB<half>); | |
| if (imfQuality == Imf::FLOAT) pixelSize = sizeof(tsRGB<float>); | |
| char *buffer = new char[uint64_t(width) * height * pixelSize]; | |
| tsRGB<half> *halfPointer = reinterpret_cast<tsRGB<half> *>(buffer); | |
| tsRGB<float> *floatPointer = reinterpret_cast<tsRGB<float> *>(buffer); | |
| for (uint64_t y = 0; y < height; y++) | |
| { | |
| for (uint64_t x = 0; x < width; x++) | |
| { | |
| uint64_t ptr = x + y * width; | |
| if (imfQuality == Imf::FLOAT) | |
| { | |
| sRGBFloat pixel = image->GetPixelImage(x, y); | |
| floatPointer[ptr].R = pixel.R; | |
| floatPointer[ptr].G = pixel.G; | |
| floatPointer[ptr].B = pixel.B; | |
| } | |
| else | |
| { | |
| sRGBFloat pixel = image->GetPixelImage(x, y); | |
| halfPointer[ptr].R = pixel.R; | |
| halfPointer[ptr].G = pixel.G; | |
| halfPointer[ptr].B = pixel.B; | |
| } | |
| } | |
| } | |
| // point EXR frame buffer to rgb | |
| size_t compSize = (imfQuality == Imf::FLOAT ? sizeof(float) : sizeof(half)); | |
| frameBuffer.insert("R", Imf::Slice(imfQuality, static_cast<char *>(buffer) + 0 * compSize, | |
| 3 * compSize, 3 * width * compSize)); | |
| frameBuffer.insert("G", Imf::Slice(imfQuality, static_cast<char *>(buffer) + 1 * compSize, | |
| 3 * compSize, 3 * width * compSize)); | |
| frameBuffer.insert("B", Imf::Slice(imfQuality, static_cast<char *>(buffer) + 2 * compSize, | |
| 3 * compSize, 3 * width * compSize)); | |
| } | |
| if (imageConfig.contains(IMAGE_CONTENT_ALPHA)) | |
| { | |
| // add alpha channel header | |
| Imf::PixelType imfQuality = | |
| imageConfig[IMAGE_CONTENT_ALPHA].channelQuality == IMAGE_CHANNEL_QUALITY_32 ? Imf::FLOAT | |
| : Imf::HALF; | |
| header.channels().insert("A", Imf::Channel(imfQuality)); | |
| int pixelSize = sizeof(half); | |
| if (imfQuality == Imf::FLOAT) pixelSize = sizeof(float); | |
| char *buffer = new char[uint64_t(width) * height * pixelSize]; | |
| half *halfPointer = reinterpret_cast<half *>(buffer); | |
| float *floatPointer = reinterpret_cast<float *>(buffer); | |
| for (uint64_t y = 0; y < height; y++) | |
| { | |
| for (uint64_t x = 0; x < width; x++) | |
| { | |
| uint64_t ptr = x + y * width; | |
| if (imfQuality == Imf::FLOAT) | |
| { | |
| floatPointer[ptr] = image->GetPixelAlpha(x, y) / 65536.0; | |
| } | |
| else | |
| { | |
| halfPointer[ptr] = image->GetPixelAlpha(x, y) / 65536.0; | |
| } | |
| } | |
| } | |
| // point EXR frame buffer to alpha | |
| size_t compSize = (imfQuality == Imf::FLOAT ? sizeof(float) : sizeof(half)); | |
| frameBuffer.insert( | |
| "A", Imf::Slice(imfQuality, static_cast<char *>(buffer), compSize, width * compSize)); | |
| } | |
| if (imageConfig.contains(IMAGE_CONTENT_ZBUFFER)) | |
| { | |
| // add z Buffer channel header | |
| Imf::PixelType imfQuality = | |
| imageConfig[IMAGE_CONTENT_ZBUFFER].channelQuality == IMAGE_CHANNEL_QUALITY_32 ? Imf::FLOAT | |
| : Imf::HALF; | |
| header.channels().insert("Z", Imf::Channel(imfQuality)); | |
| // point EXR frame buffer to z buffer | |
| if (imfQuality == Imf::FLOAT) | |
| { | |
| // direct on buffer | |
| float *zBuffer = image->GetZBufferPtr(); | |
| frameBuffer.insert("Z", Imf::Slice(Imf::FLOAT, reinterpret_cast<char *>(zBuffer), | |
| sizeof(float), width * sizeof(float))); | |
| } | |
| else | |
| { | |
| int pixelSize = sizeof(half); | |
| char *buffer = new char[uint64_t(width) * height * pixelSize]; | |
| half *halfPointer = reinterpret_cast<half *>(buffer); | |
| for (uint64_t y = 0; y < height; y++) | |
| { | |
| for (uint64_t x = 0; x < width; x++) | |
| { | |
| uint64_t ptr = x + y * width; | |
| halfPointer[ptr] = image->GetPixelZBuffer(x, y); | |
| } | |
| } | |
| frameBuffer.insert("Z", | |
| Imf::Slice(imfQuality, static_cast<char *>(buffer), sizeof(half), width * sizeof(half))); | |
| } | |
| } | |
| if (imageConfig.contains(IMAGE_CONTENT_NORMAL)) | |
| { | |
| // add rgb channel header | |
| Imf::PixelType imfQuality = | |
| imageConfig[IMAGE_CONTENT_NORMAL].channelQuality == IMAGE_CHANNEL_QUALITY_32 ? Imf::FLOAT | |
| : Imf::HALF; | |
| header.channels().insert("n.X", Imf::Channel(imfQuality)); | |
| header.channels().insert("n.Y", Imf::Channel(imfQuality)); | |
| header.channels().insert("n.Z", Imf::Channel(imfQuality)); | |
| int pixelSize = sizeof(tsRGB<half>); | |
| if (imfQuality == Imf::FLOAT) pixelSize = sizeof(tsRGB<float>); | |
| char *buffer = new char[uint64_t(width) * height * pixelSize]; | |
| tsRGB<half> *halfPointer = reinterpret_cast<tsRGB<half> *>(buffer); | |
| tsRGB<float> *floatPointer = reinterpret_cast<tsRGB<float> *>(buffer); | |
| for (uint64_t y = 0; y < height; y++) | |
| { | |
| for (uint64_t x = 0; x < width; x++) | |
| { | |
| uint64_t ptr = (x + y * width); | |
| sRGBFloat pixel = image->GetPixelNormal(x, y); | |
| if (imfQuality == Imf::FLOAT) | |
| { | |
| floatPointer[ptr] = pixel; | |
| } | |
| else | |
| { | |
| halfPointer[ptr].R = pixel.R; | |
| halfPointer[ptr].G = pixel.G; | |
| halfPointer[ptr].B = pixel.B; | |
| } | |
| } | |
| } | |
| // point EXR frame buffer to rgb | |
| size_t compSize = (imfQuality == Imf::FLOAT ? sizeof(float) : sizeof(half)); | |
| frameBuffer.insert("n.X", Imf::Slice(imfQuality, static_cast<char *>(buffer) + 0 * compSize, | |
| 3 * compSize, 3 * width * compSize)); | |
| frameBuffer.insert("n.Y", Imf::Slice(imfQuality, static_cast<char *>(buffer) + 1 * compSize, | |
| 3 * compSize, 3 * width * compSize)); | |
| frameBuffer.insert("n.Z", Imf::Slice(imfQuality, static_cast<char *>(buffer) + 2 * compSize, | |
| 3 * compSize, 3 * width * compSize)); | |
| } | |
| Imf::OutputFile file(filename.toStdString().c_str(), header); | |
| file.setFrameBuffer(frameBuffer); | |
| // file.writePixels(height); | |
| for (uint64_t r = 0; r < height; r += SAVE_CHUNK_SIZE) | |
| { | |
| uint64_t currentChunkSize = min(height - r, SAVE_CHUNK_SIZE); | |
| file.writePixels(currentChunkSize); | |
| emit updateProgressAndStatus(getJobName(), QString("Saving all channels"), 1.0 * r / height); | |
| } | |
| } | |
| #endif /* USE_EXR */ | |
| #ifdef USE_TIFF | |
| bool ImageFileSaveTIFF::SaveTIFF( | |
| QString filenameInput, cImage *image, structSaveImageChannel imageChannel, bool appendAlpha) | |
| { | |
| uint64_t width = image->GetWidth(); | |
| uint64_t height = image->GetHeight(); | |
| TIFF *tiff = TIFFOpen(filenameInput.toLocal8Bit().constData(), "w"); | |
| if (!tiff) | |
| { | |
| qCritical() << "SaveTiff() cannot open file"; | |
| return false; | |
| } | |
| int qualitySize; | |
| int sampleFormat; | |
| switch (imageChannel.channelQuality) | |
| { | |
| case IMAGE_CHANNEL_QUALITY_8: | |
| qualitySize = 8; | |
| sampleFormat = SAMPLEFORMAT_UINT; | |
| break; | |
| case IMAGE_CHANNEL_QUALITY_16: | |
| qualitySize = 16; | |
| sampleFormat = SAMPLEFORMAT_UINT; | |
| break; | |
| default: | |
| qualitySize = 32; | |
| sampleFormat = SAMPLEFORMAT_IEEEFP; | |
| break; | |
| } | |
| int colorType; | |
| switch (imageChannel.contentType) | |
| { | |
| case IMAGE_CONTENT_COLOR: colorType = PHOTOMETRIC_RGB; break; | |
| case IMAGE_CONTENT_ALPHA: | |
| case IMAGE_CONTENT_ZBUFFER: colorType = PHOTOMETRIC_MINISBLACK; break; | |
| case IMAGE_CONTENT_NORMAL: colorType = PHOTOMETRIC_RGB; break; | |
| default: colorType = PHOTOMETRIC_RGB; break; | |
| } | |
| int samplesPerPixel = IMAGE_CONTENT_COLOR; | |
| switch (imageChannel.contentType) | |
| { | |
| case IMAGE_CONTENT_COLOR: samplesPerPixel = appendAlpha ? 4 : 3; break; | |
| case IMAGE_CONTENT_ALPHA: samplesPerPixel = 1; break; | |
| case IMAGE_CONTENT_ZBUFFER: samplesPerPixel = 1; break; | |
| case IMAGE_CONTENT_NORMAL: samplesPerPixel = 3; break; | |
| } | |
| TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width); | |
| TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height); | |
| TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, qualitySize); | |
| TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, samplesPerPixel); | |
| TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, SAVE_CHUNK_SIZE); | |
| TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE); | |
| TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, colorType); | |
| TIFFSetField(tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB); | |
| TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); | |
| TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, sampleFormat); | |
| uint64_t pixelSize = samplesPerPixel * qualitySize / 8; | |
| char *colorPtr = new char[uint64_t(width) * height * pixelSize]; | |
| // calculate min / max values from zbuffer range | |
| float minZ = float(1.0e50); | |
| float maxZ = 0.0; | |
| float rangeZ = 0.0; | |
| if (imageChannel.contentType == IMAGE_CONTENT_ZBUFFER) | |
| { | |
| float *zbuffer = image->GetZBufferPtr(); | |
| uint64_t size = width * height; | |
| for (uint64_t i = 0; i < size; i++) | |
| { | |
| float z = zbuffer[i]; | |
| if (z > maxZ && z < 1e19) maxZ = z; | |
| if (z < minZ) minZ = z; | |
| } | |
| rangeZ = maxZ - minZ; | |
| } | |
| for (uint64_t y = 0; y < height; y++) | |
| { | |
| for (uint64_t x = 0; x < width; x++) | |
| { | |
| uint64_t ptr = (x + y * width) * pixelSize; | |
| switch (imageChannel.contentType) | |
| { | |
| case IMAGE_CONTENT_COLOR: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_32) | |
| { | |
| if (appendAlpha) | |
| { | |
| sRGBAfloat *typedColorPtr = reinterpret_cast<sRGBAfloat *>(&colorPtr[ptr]); | |
| sRGB16 rgbPointer = image->GetPixelImage16(x, y); | |
| typedColorPtr->R = rgbPointer.R / 65536.0; | |
| typedColorPtr->G = rgbPointer.G / 65536.0; | |
| typedColorPtr->B = rgbPointer.B / 65536.0; | |
| typedColorPtr->A = image->GetPixelAlpha(x, y) / 65536.0; | |
| } | |
| else | |
| { | |
| sRGBFloat *typedColorPtr = reinterpret_cast<sRGBFloat *>(&colorPtr[ptr]); | |
| sRGB16 rgbPointer = image->GetPixelImage16(x, y); | |
| typedColorPtr->R = rgbPointer.R / 65536.0; | |
| typedColorPtr->G = rgbPointer.G / 65536.0; | |
| typedColorPtr->B = rgbPointer.B / 65536.0; | |
| } | |
| } | |
| else if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| if (appendAlpha) | |
| { | |
| sRGBA16 *typedColorPtr = reinterpret_cast<sRGBA16 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGBA16(image->GetPixelImage16(x, y)); | |
| typedColorPtr->A = image->GetPixelAlpha(x, y); | |
| } | |
| else | |
| { | |
| sRGB16 *typedColorPtr = reinterpret_cast<sRGB16 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGB16(image->GetPixelImage16(x, y)); | |
| } | |
| } | |
| else | |
| { | |
| if (appendAlpha) | |
| { | |
| if (x == 0 && y == 0) | |
| { | |
| image->ConvertAlphaTo8bit(); | |
| image->ConvertTo8bit(); | |
| } | |
| sRGBA8 *typedColorPtr = reinterpret_cast<sRGBA8 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGBA8(image->GetPixelImage8(x, y)); | |
| typedColorPtr->A = image->GetPixelAlpha8(x, y); | |
| } | |
| else | |
| { | |
| if (x == 0 && y == 0) | |
| { | |
| image->ConvertTo8bit(); | |
| } | |
| sRGB8 *typedColorPtr = reinterpret_cast<sRGB8 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGB8(image->GetPixelImage8(x, y)); | |
| } | |
| } | |
| } | |
| break; | |
| case IMAGE_CONTENT_ALPHA: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_32) | |
| { | |
| float *typedColorPtr = reinterpret_cast<float *>(&colorPtr[ptr]); | |
| *typedColorPtr = image->GetPixelAlpha(x, y) / 65536.0; | |
| } | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| unsigned short *typedColorPtr = reinterpret_cast<unsigned short *>(&colorPtr[ptr]); | |
| *typedColorPtr = image->GetPixelAlpha(x, y); | |
| } | |
| else | |
| { | |
| if (x == 0 && y == 0) | |
| { | |
| image->ConvertAlphaTo8bit(); | |
| } | |
| unsigned char *typedColorPtr = reinterpret_cast<unsigned char *>(&colorPtr[ptr]); | |
| *typedColorPtr = image->GetPixelAlpha8(x, y); | |
| } | |
| } | |
| break; | |
| case IMAGE_CONTENT_ZBUFFER: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_32) | |
| { | |
| float *typedColorPtr = reinterpret_cast<float *>(&colorPtr[ptr]); | |
| *typedColorPtr = (image->GetPixelZBuffer(x, y) - minZ) / rangeZ; | |
| } | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| unsigned short *typedColorPtr = reinterpret_cast<unsigned short *>(&colorPtr[ptr]); | |
| *typedColorPtr = | |
| static_cast<unsigned short>(((image->GetPixelZBuffer(x, y) - minZ) / rangeZ) * 65535); | |
| } | |
| else | |
| { | |
| unsigned char *typedColorPtr = reinterpret_cast<unsigned char *>(&colorPtr[ptr]); | |
| *typedColorPtr = | |
| static_cast<unsigned char>(((image->GetPixelZBuffer(x, y) - minZ) / rangeZ) * 255); | |
| } | |
| } | |
| break; | |
| case IMAGE_CONTENT_NORMAL: | |
| { | |
| if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_32) | |
| { | |
| sRGBFloat *typedColorPtr = reinterpret_cast<sRGBFloat *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGBFloat(image->GetPixelNormal(x, y)); | |
| } | |
| else if (imageChannel.channelQuality == IMAGE_CHANNEL_QUALITY_16) | |
| { | |
| if (x == 0 && y == 0) image->ConvertNormalTo16Bit(); | |
| sRGB16 *typedColorPtr = reinterpret_cast<sRGB16 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGB16(image->GetPixelNormal16(x, y)); | |
| } | |
| else | |
| { | |
| if (x == 0 && y == 0) image->ConvertNormalTo8Bit(); | |
| sRGB8 *typedColorPtr = reinterpret_cast<sRGB8 *>(&colorPtr[ptr]); | |
| *typedColorPtr = sRGB8(image->GetPixelNormal8(x, y)); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| // TIFFWriteEncodedStrip( | |
| // tiff, 0, static_cast<void *>(colorPtr), tsize_t(width * height * pixelSize)); | |
| for (uint64_t r = 0; r < height; r += SAVE_CHUNK_SIZE) | |
| { | |
| uint64_t currentChunkSize = min(height - r, SAVE_CHUNK_SIZE); | |
| // needs buffer with offset position | |
| char *buf = static_cast<char *>(colorPtr) + r * pixelSize * width; | |
| tsize_t size = tsize_t(currentChunkSize * pixelSize * width); | |
| TIFFWriteEncodedStrip(tiff, r / SAVE_CHUNK_SIZE, buf, size); | |
| emit updateProgressAndStatusChannel(1.0 * r / height); | |
| } | |
| TIFFClose(tiff); | |
| delete[] colorPtr; | |
| return true; | |
| } | |
| #endif /* USE_TIFF */ |