MakoPartialImage.cpp
CPP
/* -----------------------------------------------------------------------
* <copyright file="MakoPartialImage.cpp.cpp" company="Global Graphics Software Ltd">
* Copyright (c) 2022-2024 Global Graphics Software Ltd. All rights reserved.
* </copyright>
* <summary>
* This example is provided on an "as is" basis and without warranty of any kind.
* Global Graphics Software Ltd. does not warrant or make any representations
* regarding the use or results of use of this example.
* </summary>
* -----------------------------------------------------------------------
*/
#include <iostream>
#include <jawsmako/jawsmako.h>
#include <jawsmako/pdfoutput.h>
using namespace JawsMako;
using namespace EDL;
IDOMImagePtr getPartialImage(const IJawsMakoPtr& mako, IDOMImagePtr image, const FRect& subImageRect);
int main()
{
try
{
const auto mako = IJawsMako::create();
mako->enableAllFeatures(mako);
const U8String testFilePath = R"(..\..\TestFiles\)";
const IDOMImagePtr image =
IDOMJPEGImage::create(mako, IInputStream::createFromFile(mako, testFilePath + "WEV_086.JPG"));
const auto partialImage = getPartialImage(mako, image, FRect(230, 230, 400, 250));
IDOMPNGImage::encode(mako, partialImage, IOutputStream::createToFile(mako, "JustTheKayak.png"));
}
catch (IError& e)
{
const String errorFormatString = getEDLErrorString(e.getErrorCode());
std::wcerr << L"Exception thrown: " << e.getErrorDescription(errorFormatString) << std::endl;
return static_cast<int>(e.getErrorCode());
}
catch (std::exception& e)
{
std::wcerr << L"std::exception thrown: " << e.what() << std::endl;
return 1;
}
return 0;
}
/// <summary>
/// Extract part of an image, the area of which is described by an FRect where:
/// x,y are the top left corner, in pixels
/// dX, dY are the width and height
/// </summary>
/// <param name="mako"></param>
/// <param name="image"></param>
/// <param name="subImageRect"></param>
/// <returns>The resulting IDOMImage</returns>
IDOMImagePtr getPartialImage(const IJawsMakoPtr& mako, IDOMImagePtr image, const FRect& subImageRect)
{
// Get some details of the original image
auto imageFrame = image->getImageFrame(mako);
auto bps = imageFrame->getBPS();
if (bps < 8)
{
image = IDOMFilteredImage::create(mako, image, IDOMImageBitScalerFilter::create(mako, 8));
imageFrame = image->getImageFrame(mako);
bps = 8;
}
else if (bps != 8 && bps != 16)
{
image = IDOMFilteredImage::create(mako, image, IDOMImageBitScalerFilter::create(mako, 16));
imageFrame = image->getImageFrame(mako);
bps = 16;
}
const auto colorSpace = imageFrame->getColorSpace();
const auto stride = imageFrame->getRawBytesPerRow();
const auto bpp = imageFrame->getNumChannels() * bps / 8;
// Check the requested area is within the bounds of the original
const auto originalImageRect = new FRect(0.0, 0.0, imageFrame->getWidth(), imageFrame->getHeight());
if (!originalImageRect->containsRect(subImageRect))
return {};
IRAInputStreamPtr reader;
IRAOutputStreamPtr writer;
mako->getTempStore()->createTemporaryReaderWriterPair(reader, writer);
const IInputStreamPtr inStream = IInputStream::createFromLz4Compressed(mako, reader);
const IOutputStreamPtr outStream = IOutputStream::createToLz4Compressed(mako, writer);
IImageFrameWriterPtr frameWriter;
auto subImage = IDOMRawImage::createWriterAndImage(
mako,
frameWriter,
colorSpace,
static_cast<uint32>(subImageRect.dX),
static_cast<uint32>(subImageRect.dY),
bps,
imageFrame->getXResolution(),
imageFrame->getYResolution(),
eImageExtraChannelType::eIECNone,
inStream, outStream);
CEDLSimpleBuffer rowBuffer(stride);
// Move row pointer down to the first row of the partial image
imageFrame->skipScanLines(static_cast<uint32>(subImageRect.y));
// Copy the portion of the scanlines that we need
for (uint32 i = 0; i < static_cast<uint32>(subImageRect.dY); i++)
{
imageFrame->readScanLine(&rowBuffer[0], rowBuffer.size());
frameWriter->writeScanLine(&rowBuffer[static_cast<uint32>(subImageRect.x) * bpp]);
}
frameWriter->flushData();
return subImage;
}
|
MakoPartialImage.cs
C#
/* -----------------------------------------------------------------------
* <copyright file="MakoPartialImage.cs" company="Global Graphics Software Ltd">
* Copyright (c) 2022-2024 Global Graphics Software Ltd. All rights reserved.
* </copyright>
* <summary>
* This example is provided on an "as is" basis and without warranty of any kind.
* Global Graphics Software Ltd. does not warrant or make any representations
* regarding the use or results of use of this example.
* </summary>
* -----------------------------------------------------------------------
*/
using JawsMako;
namespace MakoPartialImage;
internal class Program
{
static void Main(string[] args)
{
try
{
var mako = IJawsMako.create();
IJawsMako.enableAllFeatures(mako);
var testFilePath = @"..\..\..\..\TestFiles\";
IDOMImage image = IDOMJPEGImage.create(mako,
IInputStream.createFromFile(mako, testFilePath + "WEV_086.JPG"));
var partialImage = GetPartialImage(mako, image, new FRect(230, 230, 400, 250));
IDOMPNGImage.encode(mako, partialImage, IOutputStream.createToFile(mako, "JustTheKayak.png"));
}
catch (MakoException e)
{
Console.WriteLine($"Mako exception thrown: {e.m_msg}");
}
catch (Exception e)
{
Console.WriteLine($"Exception thrown: {e}");
}
}
/// <summary>
/// Extract part of an image, the area of which is described by an FRect where:
/// x,y are the top left corner, in pixels
/// dX, dY are the width and height
/// </summary>
/// <param name="mako"></param>
/// <param name="image"></param>
/// <param name="subImageRect"></param>
/// <returns>The resulting IDOMImage</returns>
static IDOMImage GetPartialImage(IJawsMako mako, IDOMImage image, FRect subImageRect)
{
// Get some details of the original image
var imageFrame = image.getImageFrame(mako);
var bps = imageFrame.getBPS();
if (bps < 8)
{
image = IDOMFilteredImage.create(mako, image, IDOMImageBitScalerFilter.create(mako, 8));
imageFrame = image.getImageFrame(mako);
bps = 8;
}
else if (bps != 8 && bps != 16)
{
image = IDOMFilteredImage.create(mako, image, IDOMImageBitScalerFilter.create(mako, 16));
imageFrame = image.getImageFrame(mako);
bps = 16;
}
var colorSpace = imageFrame.getColorSpace();
var stride = imageFrame.getRawBytesPerRow();
var bpp = imageFrame.getNumChannels() * bps / 8;
// Check the requested area is within the bounds of the original
var originalImageRect = new FRect(0.0, 0.0, imageFrame.getWidth(), imageFrame.getHeight());
if (!originalImageRect.containsRect(subImageRect))
return IDOMImage.Null();
var (reader, writer) = mako.getTempStore().createTemporaryReaderWriterTuple();
IInputStream inStream = IInputStream.createFromLz4Compressed(mako, reader);
IOutputStream outStream = IOutputStream.createToLz4Compressed(mako, writer);
var imageAndWriter = IDOMRawImage.createWriterAndImage(
mako,
colorSpace,
(uint)subImageRect.dX,
(uint)subImageRect.dY,
bps,
imageFrame.getXResolution(),
imageFrame.getYResolution(),
eImageExtraChannelType.eIECNone,
inStream, outStream);
var subImage = imageAndWriter.domImage;
var frameWriter = imageAndWriter.frameWriter;
imageAndWriter.domImage = null;
imageAndWriter.frameWriter = null;
var rowBuffer = new byte[stride];
var targetRowBuffer = new byte[(uint)subImageRect.dX * bpp];
// Move row pointer down to the first row of the partial image
imageFrame.skipScanLines((uint)subImageRect.y);
// Copy the portion of the scanlines that we need
for (uint i = 0; i < (uint)subImageRect.dY; i++)
{
imageFrame.readScanLine(rowBuffer);
Array.Copy(rowBuffer, (uint)subImageRect.x * bpp, targetRowBuffer, 0, targetRowBuffer.Length);
frameWriter.writeScanLine(targetRowBuffer);
}
frameWriter.flushData();
return subImage;
}
}
|
Custom TIFF image reader
CPP
/* -----------------------------------------------------------------------------------
* <copyright file="MakoImage2PDF.cpp" company="Global Graphics Software Ltd">
* Copyright (c) 2022-2024 Global Graphics Software Ltd. All rights reserved.
* </copyright>
* <summary>
* This example is provided on an "as is" basis and without warranty of any kind.
* Global Graphics Software Ltd. does not warrant or make any representations
* regarding the use or results of use of this example.
*
* Simple sample application to load an image into a new PDF using the Mako APIs.
* </summary>
* -----------------------------------------------------------------------------------
*/
#include <exception>
#include <iostream>
#include <jawsmako/jawsmako.h>
#include <jawsmako/pdfoutput.h>
#include <edl/idompath.h>
#include "ExtGamutTiffLoader.h"
#ifdef _WIN32
#include <fcntl.h>
#include <corecrt_io.h>
#endif
#include <filesystem>
namespace fs = std::filesystem;
using namespace JawsMako;
using namespace EDL;
enum eImageFormat
{
eIFJPG,
eIFPNG,
eIFTIF,
eIFPSD
};
// Determine the associated format for a given path from the file extension
static eImageFormat imageFormatFromPath(const String& sPath)
{
const auto path = fs::path(sPath);
if (path.has_extension())
{
std::string extension = path.extension().string();
std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
if (extension == ".jpg" || extension == ".jpeg")
return eIFJPG;
if (extension == ".png")
return eIFPNG;
if (extension == ".tif" || extension == ".tiff")
return eIFTIF;
if (extension == ".psd")
return eIFPSD;
}
std::string message("Unsupported file type for path ");
message += StringToU8String(sPath).c_str();
throw std::invalid_argument(message);
}
// Image type identifier string
static U8String imageType(eImageFormat imgF)
{
switch(imgF)
{
case eIFJPG:
return "jpeg";
case eIFPNG:
return "png";
case eIFTIF:
return "tiff";
case eIFPSD:
return "psd";
}
return "";
}
#ifdef _WIN32
int wmain(int argc, wchar_t* argv[])
{
_setmode(_fileno(stderr), _O_U16TEXT);
_setmaxstdio(2048);
#else
int main(int argc, char* argv[])
{
#endif
try
{
// Create our JawsMako instance.
const IJawsMakoPtr jawsMako = IJawsMako::create();
IJawsMako::enableAllFeatures(jawsMako);
// There should be at least one argument, a source file
if (argc < 2)
{
std::wcout << "Mako Image to PDF v1.2.0\n" << std::endl;
std::wcout << "Usage:" << argv[0] << " <source file.jpg|.png|.tif|.psd> [<output file.pdf>]" << std::endl;
return 1;
}
// Most JawsMako APIs can take UTF8 or Wide Character paths.
String sInputFilePath;
fs::path pOutputFilePath;
#ifdef _WIN32
sInputFilePath = String(argv[1]);
#else
sInputFilePath = U8StringToString(argv[1]);
#endif
const auto imageFormat = imageFormatFromPath(sInputFilePath);
pOutputFilePath = argc > 2 ? fs::path(argv[2]) : fs::path(sInputFilePath).replace_extension(".pdf");
// Modify filename to include a marker for the image format that it was converted from
auto modFilename = pOutputFilePath.stem();
modFilename += "_";
modFilename += imageType(imageFormat);
modFilename += ".pdf";
auto sOutputFilePath = U8String(pOutputFilePath.replace_filename(modFilename).u8string());
// Report progress
std::wcout << L"Processing \'" << sInputFilePath << L"\'..." << std::endl;
// Load the image from the local file
IDOMImagePtr image;
switch (imageFormat)
{
case eIFJPG:
image = IDOMJPEGImage::create(jawsMako, IInputStream::createFromFile(jawsMako, sInputFilePath));
break;
case eIFPNG:
image = IDOMPNGImage::create(jawsMako, IInputStream::createFromFile(jawsMako, sInputFilePath));
break;
case eIFTIF:
image = ExtGamutTiffLoader::load(jawsMako, sInputFilePath);
break;
case eIFPSD:
image = IDOMPSDImage::create(jawsMako, IInputStream::createFromFile(jawsMako, sInputFilePath));
break;
}
// Get image attributes
const IImageFramePtr imageFrame = image->getImageFrame(jawsMako);
uint8_t numChannels = imageFrame->getNumChannels();
const bool hasAlphaChan = imageFrame->getHasAlphaChannel();
if (hasAlphaChan)
numChannels--;
const double width = imageFrame->getWidth() / imageFrame->getXResolution() * 96.0;
const double height = imageFrame->getHeight() / imageFrame->getYResolution() * 96.0;
#if(0)
IDOMFilteredImagePtr filteredImage;
switch (noOfChannels)
{
case 1:
{
// Create a filter that will convert the image to DeviceGray
const IDOMImageColorSpaceSubstitutionFilterPtr colorSpaceSubstitutionFilter = IDOMImageColorSpaceSubstitutionFilter::create(jawsMako, IDOMColorSpaceDeviceGray::create(jawsMako));
filteredImage = IDOMFilteredImage::create(jawsMako, image, colorSpaceSubstitutionFilter);
}
break;
case 3:
{
// Create a filter that will convert the image to DeviceRGB
const IDOMImageColorSpaceSubstitutionFilterPtr colorSpaceSubstitutionFilter = IDOMImageColorSpaceSubstitutionFilter::create(jawsMako, IDOMColorSpaceDeviceRGB::create(jawsMako));
filteredImage = IDOMFilteredImage::create(jawsMako, image, colorSpaceSubstitutionFilter);
}
break;
case 4:
{
// Create a filter that will convert the image to DeviceCMYK
const IDOMImageColorSpaceSubstitutionFilterPtr colorSpaceSubstitutionFilter = IDOMImageColorSpaceSubstitutionFilter::create(jawsMako, IDOMColorSpaceDeviceCMYK::create(jawsMako));
filteredImage = IDOMFilteredImage::create(jawsMako, image, colorSpaceSubstitutionFilter);
}
break;
default:
std::cout << inputFilePath << " is not a 1-, 3- or 4-channel image. Expecting an 8-bit grayscale, RGB or CMYK image." << std::endl;
return 1;
}
image = filteredImage;
#endif
// Convert CMYK JPEG to RGB
if (imageFormat == eIFJPG && numChannels == 4)
{
CEDLVector<IDOMImageFilterPtr> invertAndConvert;
invertAndConvert.append(IDOMImageInverterFilter::create(jawsMako));
invertAndConvert.append(IDOMImageColorConverterFilter::create(jawsMako, IDOMColorSpaceDeviceRGB::create(jawsMako), eRelativeColorimetric, eBPCDefault));
image = IDOMFilteredImage::create(jawsMako, image, invertAndConvert);
numChannels = 3;
}
// Create an empty assembly, document, and page.
IDocumentAssemblyPtr assembly = IDocumentAssembly::create(jawsMako);
IDocumentPtr document = IDocument::create(jawsMako);
IPagePtr page = IPage::create(jawsMako);
// Add the page to the document, and the document to the assembly
document->appendPage(page);
assembly->appendDocument(document);
// Create a fixed page that matches the image size
// Mako units are in 96ths of an inch.
IDOMFixedPagePtr fixedPage = IDOMFixedPage::create(jawsMako, width, height);
// Position the image on the page, with no margin
const FRect imageBounds(
0.0, 0.0, // origin (x, y)
width, height); // width and height
// Create a path directly from the image
const IDOMPathNodePtr path = IDOMPathNode::createImage(jawsMako, image, imageBounds);
// And add the path to the page
fixedPage->appendChild(path);
// This becomes the page contents
page->setContent(fixedPage);
// Now we can write this to an PDF
IPDFOutputPtr pdfOutput = IPDFOutput::create(jawsMako);
switch (numChannels)
{
case 1:
pdfOutput->setParameter("TargetColorSpace", "DeviceGray");
break;
case 3:
pdfOutput->setParameter("TargetColorSpace", "DeviceRGB");
break;
case 4:
pdfOutput->setParameter("TargetColorSpace", "DeviceCMYK");
break;
default:;
}
pdfOutput->setPreferredGrayImageCompression(IPDFOutput::eICFlate);
pdfOutput->setPreferredColorImageCompression(IPDFOutput::eICFlate);
pdfOutput->writeAssembly(assembly, sOutputFilePath);
std::wcout << L"Output file '";
std::wcerr << U8StringToString(sOutputFilePath);
std::wcout << L"' written.";
std::wcerr << std::endl;
}
catch (IError& e)
{
const String errorFormatString = getEDLErrorString(e.getErrorCode());
std::wcerr << L"Exception thrown: " << e.getErrorDescription(errorFormatString) << std::endl;
return static_cast<int>(e.getErrorCode());
}
catch (std::exception& e)
{
std::wcerr << L"std::exception thrown: " << e.what() << std::endl;
return 1;
}
return 0;
}
CPP
/* -----------------------------------------------------------------------
* <copyright file="ExtGamutTiffLoader.h" company="Global Graphics Software Ltd">
* Copyright (c) 2024 Global Graphics Software Ltd. All rights reserved.
* </copyright>
* <summary>
* This example is provided on an "as is" basis and without warranty of any kind.
* Global Graphics Software Ltd. does not warrant or make any representations
* regarding the use or results of use of this example.
* </summary>
* -----------------------------------------------------------------------
*/
#pragma once
#include <iostream>
#include <jawsmako/jawsmako.h>
#include <jawsmako/pdfoutput.h>
#include <edl/edlvector.h>
#include <edl/edlstring.h>
#include <edl/edlsimplebuffer.h>
#include <edl/platform.h>
#include <edl/idompath.h>
#include <tiff.h>
#include <tiffconf.h>
#include <tiffio.h>
using namespace JawsMako;
using namespace EDL;
class ExtGamutTiffLoader
{
public:
static IDOMImagePtr load(const IJawsMakoPtr& mako, const String& tiffFile);
private:
// See Appendix A in Photoshop API Guide.pdf.
typedef struct
{
uint16_t colorSpace;
uint16_t color[4];
uint16_t opacity;
uint8_t kind;
} DisplayInfo;
static void skipBytes(uint8_t*& ptr, uint32_t n, uint8_t*& end);
static uint8_t read8(uint8_t*& ptr, uint8_t*& end);
static uint16_t read16(uint8_t* ptr);
static uint16_t read16(uint8_t*& ptr, uint8_t*& end);
static uint32_t read32(uint8_t* ptr);
static uint32_t read32(uint8_t*& ptr, uint8_t*& end);
static RawString readPascalString(uint8_t*& ptr, uint8_t*& end);
static U8String readUnicodeString(uint8_t*& ptr, uint8_t*& end);
static DisplayInfo readDisplayInfo(uint8_t*& ptr, uint8_t*& end);
static IDOMColorSpaceDeviceNPtr createDeviceNCMYKSpace(const IJawsMakoPtr& mako,
const CRawStringVect& colorantNames,
const CEDLVector<DisplayInfo>& displayInfos);
};
CPP
/* -----------------------------------------------------------------------
* <copyright file="ExtGamutTiffLoader.cpp" company="Global Graphics Software Ltd">
* Copyright (c) 2024 Global Graphics Software Ltd. All rights reserved.
* </copyright>
* <summary>
* This example is provided on an "as is" basis and without warranty of any kind.
* Global Graphics Software Ltd. does not warrant or make any representations
* regarding the use or results of use of this example.
* </summary>
* -----------------------------------------------------------------------
*/
#include "ExtGamutTiffLoader.h"
using namespace JawsMako;
using namespace EDL;
IDOMImagePtr ExtGamutTiffLoader::load(const IJawsMakoPtr& mako, const String& tiffFile)
{
try
{
// Open the TIFF
TIFF* tif = TIFFOpen(StringToU8String(tiffFile).c_str(), "r");
if (!tif)
throw std::runtime_error("Unable to open TIFF");
uint32_t width, height;
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
uint32_t tagLength;
uint8_t* tagBufferPtr = nullptr;
TIFFGetField(tif, TIFFTAG_PHOTOSHOP, &tagLength, &tagBufferPtr);
if (tagLength == 0)
{
TIFFClose(tif);
return IDOMTIFFImage::create(mako, IInputStream::createFromFile(mako, tiffFile));
}
CRawStringVect channelNames;
CU8StringVect unicodeChannelNames;
CEDLVector<DisplayInfo> displayInfos;
auto tagEnd = tagBufferPtr + tagLength;
while (tagBufferPtr < tagEnd)
{
const auto signature = read32(tagBufferPtr, tagEnd);
if (signature != 0x3842494D) // "8BIM"
{
throw std::runtime_error("Invalid Photoshop signature");
}
const auto imageResourceId = read16(tagBufferPtr, tagEnd);
// Pad if not even and add the byte for the length.
auto nameLength = *tagBufferPtr;
nameLength += ((nameLength & 1) ^ 1) + 1;
// Skip the name.
skipBytes(tagBufferPtr, nameLength, tagEnd);
auto resourceDataLength = read32(tagBufferPtr, tagEnd);
const auto resourceDataPtr = tagBufferPtr;
switch (imageResourceId)
{
case 0x03EE:
{
// Names of the alpha channels as a series of Pascal strings.
auto alphaChannelPtr = resourceDataPtr;
auto alphaChannelEnd = alphaChannelPtr + resourceDataLength;
while (alphaChannelPtr < alphaChannelEnd)
{
channelNames.append(readPascalString(alphaChannelPtr, alphaChannelEnd));
}
}
break;
case 0x0415:
{
// Unicode Alpha Names
auto alphaNamesPtr = resourceDataPtr;
auto alphaNamesEnd = alphaNamesPtr + resourceDataLength;
while (alphaNamesPtr < alphaNamesEnd)
{
unicodeChannelNames.append(readUnicodeString(alphaNamesPtr, alphaNamesEnd));
}
}
break;
case 0x0435:
{
// DisplayInfo structure to support floating point colours.
auto displayInfoPtr = resourceDataPtr;
auto displayInfoEnd = displayInfoPtr + resourceDataLength;
// Skip undocumented 4 bytes at the start of the data.
skipBytes(displayInfoPtr, 4, displayInfoEnd);
while (displayInfoPtr < displayInfoEnd)
{
displayInfos.append(readDisplayInfo(displayInfoPtr, displayInfoEnd));
}
}
break;
default:
;
}
// Pad to even and advance.
resourceDataLength += resourceDataLength & 1;
skipBytes(tagBufferPtr, resourceDataLength, tagEnd);
}
TIFFClose(tif);
const uint32_t numChannels = displayInfos.size();
if (numChannels == 0)
return IDOMTIFFImage::create(mako, IInputStream::createFromFile(mako, tiffFile));
if (numChannels != channelNames.size() && numChannels != unicodeChannelNames.size())
throw std::runtime_error("Invalid number of channels");
// First test the colour space.
const auto image = IDOMTIFFImage::create(mako, IInputStream::createFromFile(mako, tiffFile), 0, true);
const auto deviceN = edlobj2IDOMColorSpaceDeviceN(image->getImageFrame(mako)->getColorSpace());
// We only support DeviceCMYK or a 4 component ICCBased space.
if (!deviceN || numChannels + 4 != deviceN->getNumComponents())
{
throw std::runtime_error("Invalid color space");
}
const auto colorSpaceType = deviceN->getAlternateColorSpace()->getColorSpaceType();
if (colorSpaceType != IDOMColorSpace::eDeviceCMYK && colorSpaceType != IDOMColorSpace::eICCBased)
{
throw std::runtime_error("Invalid color space");
}
// Create a new DeviceN/CMYK color space from the Photoshop info.
// We'll use the channel names if we can, or the unicode names if not.
const auto colorSpace = createDeviceNCMYKSpace(mako, !channelNames.empty() ? channelNames : unicodeChannelNames, displayInfos);
// The spot channels are stored such that we need to invert them.
// We can do this easily by using a IDOMImageChannelSelectorFilter, which will extract
// each channel as a DeviceGray image, and then recombining them with the DeviceN color
// space we've created. But as the CMYK channels do not need to be inverted we also
// need to use a IDOMImageInverterFilter so that they are recombined back correctly.
CEDLVector<IDOMImagePtr> images;
for (uint8_t i = 0; i < 4; i++)
{
CEDLVector<IDOMImageFilterPtr> filters;
filters.append(IDOMImageChannelSelectorFilter::create(mako, IDOMImageChannelSelectorFilter::eSelectChannel, i));
filters.append(IDOMImageInverterFilter::create(mako));
images.append(IDOMFilteredImage::create(mako, image, filters));
}
for (uint8_t i = 0; i < numChannels; i++)
{
images.append(IDOMFilteredImage::create(mako, image,
IDOMImageChannelSelectorFilter::create(mako,
IDOMImageChannelSelectorFilter::eSelectChannel, 4 + i)));
}
// Recombine.
return IDOMRecombineImage::create(mako, colorSpace, images);
}
catch (IError& e)
{
const String errorFormatString = getEDLErrorString(e.getErrorCode());
std::wcerr << L"Exception thrown: " << e.getErrorDescription(errorFormatString) << std::endl;
return {};
}
catch (std::runtime_error& e)
{
std::wcerr << L"std::runtime_error thrown: " << e.what() << std::endl;
return {};
}
}
void ExtGamutTiffLoader::skipBytes(uint8_t*& ptr, uint32_t n, uint8_t*& end)
{
if (ptr + n > end)
throw std::runtime_error("Invalid Photoshop resource");
ptr += n;
}
uint8_t ExtGamutTiffLoader::read8(uint8_t*& ptr, uint8_t*& end)
{
if (ptr + 1 > end)
throw std::runtime_error("Invalid Photoshop resource");
return *ptr++;
}
uint16_t ExtGamutTiffLoader::read16(uint8_t* ptr)
{
#if EDLENDIAN == eEndianLittle // NOLINT(clang-diagnostic-undef)
return (ptr[0] << 8) | ptr[1]; // NOLINT(clang-diagnostic-implicit-int-conversion)
#else
return (ptr[1] << 8) | ptr[0];
#endif
}
uint16_t ExtGamutTiffLoader::read16(uint8_t*& ptr, uint8_t*& end)
{
if (ptr + 2 > end)
throw std::runtime_error("Invalid photoshop resource");
ptr += 2;
return read16(ptr - 2);
}
uint32_t ExtGamutTiffLoader::read32(uint8_t* ptr)
{
#if EDLENDIAN == eEndianLittle // NOLINT(clang-diagnostic-undef)
return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
#else
return (ptr[3] << 24) | (ptr[2] << 16) | (ptr[1] << 8) | ptr[0];
#endif
}
uint32_t ExtGamutTiffLoader::read32(uint8_t*& ptr, uint8_t*& end)
{
if (ptr + 4 > end)
throw std::runtime_error("Invalid photoshop resource");
ptr += 4;
return read32(ptr - 4);
}
RawString ExtGamutTiffLoader::readPascalString(uint8_t*& ptr, uint8_t*& end)
{
const auto length = *ptr;
if (ptr + length + 1 > end)
throw std::runtime_error("Invalid photoshop resource");
ptr += (length + 1);
return RawString(reinterpret_cast<char*>(ptr) - length, length);
}
U8String ExtGamutTiffLoader::readUnicodeString(uint8_t*& ptr, uint8_t*& end)
{
auto length = read32(ptr, end);
// The string should be null terminated.
// Test the string terminator.
if ((ptr + (length * sizeof(u16char)) > end) || reinterpret_cast<u16char*>(ptr)[length - 1] != 0x0000)
throw std::runtime_error("Invalid photoshop resource");
U16String unicodeString;
unicodeString.reserve(length);
for (uint32_t i = 0; i < length; i++)
{
unicodeString += static_cast<u16char>(read16(ptr));
ptr += sizeof(u16char);
}
return U16StringToU8String(unicodeString);
}
ExtGamutTiffLoader::DisplayInfo ExtGamutTiffLoader::readDisplayInfo(uint8_t*& ptr, uint8_t*& end)
{
DisplayInfo displayInfo;
displayInfo.colorSpace = read16(ptr, end);
for (unsigned short& i : displayInfo.color)
i = read16(ptr, end);
displayInfo.opacity = read16(ptr, end);
displayInfo.kind = read8(ptr, end);
return displayInfo;
}
IDOMColorSpaceDeviceNPtr ExtGamutTiffLoader::createDeviceNCMYKSpace(const IJawsMakoPtr& mako, const CRawStringVect& colorantNames, const CEDLVector<ExtGamutTiffLoader::DisplayInfo>& displayInfos)
{
// Build up our new colorants, starting with the the base colourants
auto colorants = IDOMColorSpaceDeviceN::CColorantInfoVect();
colorants.append({
IDOMColorSpaceDeviceN::CColorantInfo{ "Cyan", { 1.0, 0.0, 0.0, 0.0 } },
IDOMColorSpaceDeviceN::CColorantInfo{ "Magenta", { 0.0, 1.0, 0.0, 0.0 } },
IDOMColorSpaceDeviceN::CColorantInfo{ "Yellow", { 0.0, 0.0, 1.0, 0.0 } },
IDOMColorSpaceDeviceN::CColorantInfo{ "Black", { 0.0, 0.0, 0.0, 1.0 } }
});
// Now any spot colorants.
for (uint32_t i = 0; i < colorantNames.size(); i++)
{
const auto& displayInfo = displayInfos[i];
const auto& colorantName = colorantNames[i];
// Is it a spot? The Photoshop API Guide only documents 1 or 0,
// but looking online suggests that a spot channel will have a value of 2.
if (displayInfos[i].kind == 2)
{
switch (displayInfos[i].colorSpace)
{
case 0:
{
// RGB. The first three values in the color data are red, green, and blue.
// They are full unsigned 16-bit values as in Apple's RGBColor data
// structure. Pure red = 65535, 0, 0.
const auto color = IDOMColor::createSolidRgb(mako,
static_cast<float>(displayInfo.color[0]) / 65535.0f,
static_cast<float>(displayInfo.color[1]) / 65535.0f,
static_cast<float>(displayInfo.color[2]) / 65535.0f);
color->setColorSpace(IDOMColorSpaceDeviceCMYK::create(mako), eRelativeColorimetric, eBPCDefault, mako);
colorants.append(
IDOMColorSpaceDeviceN::CColorantInfo(
colorantName,
CEDLVector<double>({
color->getComponentValue(0),
color->getComponentValue(1),
color->getComponentValue(2),
color->getComponentValue(3)
})
)
);
}
break;
case 2:
// CMYK. The four values in the color data are cyan, magenta, yellow, and
// black. They are full unsigned 16-bit values. 0 = 100 % ink. Pure
// cyan = 0, 65535, 65535, 65535.
colorants.append(
IDOMColorSpaceDeviceN::CColorantInfo(
colorantName,
CEDLVector<double>({
1.0 - (displayInfo.color[0] / 65535.0),
1.0 - (displayInfo.color[1] / 65535.0),
1.0 - (displayInfo.color[2] / 65535.0),
1.0 - (displayInfo.color[3] / 65535.0)
})
)
);
break;
// Currently unsupported.
#if 0
case 1:
// HSV. The first three values in the color data are hue, saturation, and
// brightness. They are full unsigned 16-bit values as in Apple's
// HSVColor data structure. Pure red = 0, 65535, 65535.
break;
case 7:
// Lab. The first three values in the color data are lightness, a chrominance,
// and b chrominance. Lightness is a 16-bit value from 0...10000. The
// chromanance components are each 16-bit values from -12800...12700. Gray
// values are represented by chrominance components of 0.
// Pure white = 10000, 0, 0.
break;
// Grayscale. The first value in the color data is the gray value, from 0...10000.
case 8:
break;
#endif
default:
throw std::runtime_error("Unsupported DisplayInfo color space");
}
}
}
// Create a new DeviceN color space using the colorants.
return IDOMColorSpaceDeviceN::create(mako, colorants, IDOMColorSpaceDeviceCMYK::create(mako));
}
|