blob: 6f8983a8ad566ed4f4d8b5f94afbc7819a47700a [file]
/* -*- C++ -*-
* Copyright 2019-2025 LibRaw LLC (info@libraw.org)
*
LibRaw is free software; you can redistribute it and/or modify
it under the terms of the one of two licenses as you choose:
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
(See file LICENSE.LGPL provided in LibRaw distribution archive for details).
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
(See file LICENSE.CDDL provided in LibRaw distribution archive for details).
*/
#define LIBRAW_DNGSDK_CONFLICT 1
#include "third_party/libraw/internal/libraw_cxx_defs.h"
#ifdef USE_DNGSDK
#include "dng_host.h"
#include "dng_negative.h"
#include "dng_simple_image.h"
#include "dng_info.h"
#endif
#if defined (USE_GPRSDK) && !defined(USE_DNGSDK)
#error GPR (GoPro) SDK should be used with Adobe DNG SDK
#endif
#ifdef USE_DNGSDK
#include "dng_read_image.h"
#endif
#ifdef USE_GPRSDK
#include "gpr_read_image.h"
#endif
#ifdef USE_DNGSDK
void clear_dng_negative(void *p)
{
if (!p)
return;
dng_negative *dn = (dng_negative *)p;
delete dn;
}
void clear_dng_image(void *p)
{
if (!p) return;
dng_image *dimage = (dng_image *)p;
delete dimage;
}
static dng_ifd* search_single_ifd(const std::vector <dng_ifd *>& v, uint64 offset, int& idx, dng_stream& stream)
{
idx = -1;
for (int i = 0; i < v.size(); i++)
{
if (!v[i]) continue;
if (v[i]->fTileOffsetsOffset == offset)
{
idx = i;
return v[i];
}
else if (v[i]->fTileOffsetsCount == 1 && v[i]->fTileOffset[0] == offset)
{
idx = i;
return v[i];
}
else if (v[i]->fTileOffsetsCount > dng_ifd::kMaxTileInfo)
{
uint64 p = stream.Position();
stream.SetReadPosition(v[i]->fTileOffsetsOffset);
int32 oo = stream.TagValue_uint32(v[i]->fTileOffsetsType);
stream.SetReadPosition(p);
if (oo == offset)
{
idx = i;
return v[i];
}
}
}
return NULL;
}
static dng_ifd* search_for_ifd(const dng_info& info, uint64 offset, ushort w, ushort h, int& ifdIndex, dng_stream& stream)
{
dng_ifd *ret = 0;
ret = search_single_ifd(info.fIFD, offset, ifdIndex, stream);
int dummy;
if (!ret) ret = search_single_ifd(info.fChainedIFD, offset, dummy, stream);
if (!ret)
{
for (int c = 0; !ret && c < info.fChainedSubIFD.size(); c++)
ret = search_single_ifd(info.fChainedSubIFD[c], offset, dummy, stream);
}
if (ret && (ret->fImageLength == h) && ret->fImageWidth == w)
return ret;
ifdIndex = -1;
return 0;
}
#endif
int LibRaw::valid_for_dngsdk()
{
#ifndef USE_DNGSDK
return 0;
#else
if (!imgdata.idata.dng_version)
return 0;
if (libraw_internal_data.unpacker_data.tiff_compress == 52546) // regardless of flags or use_dngsdk value!
{
#ifdef qDNGSupportJXL
if (dngVersion_Current >= dngVersion_1_7_0_0)
return 1;
else
#endif
return 0; // Old DNG SDK
}
// All DNG larger than 2GB - to DNG SDK
if (libraw_internal_data.internal_data.input->size() > 2147483647ULL)
return 1;
if (!strcasecmp(imgdata.idata.make, "Blackmagic")
&& (libraw_internal_data.unpacker_data.tiff_compress == 7)
&& (libraw_internal_data.unpacker_data.tiff_bps > 8)
)
return 0;
if (libraw_internal_data.unpacker_data.tiff_compress == 34892
&& libraw_internal_data.unpacker_data.tiff_bps == 8
&& (libraw_internal_data.unpacker_data.tiff_samples == 3
|| libraw_internal_data.unpacker_data.tiff_samples == 1
|| libraw_internal_data.unpacker_data.tiff_samples == 4 )
&& load_raw == &LibRaw::lossy_dng_load_raw
)
{
if (!dnghost)
return 0;
try
{
dng_host *host = static_cast<dng_host *>(dnghost);
libraw_dng_stream stream(libraw_internal_data.internal_data.input);
AutoPtr<dng_negative> negative;
negative.Reset(host->Make_dng_negative());
dng_info info;
info.Parse(*host, stream);
info.PostParse(*host);
if (!info.IsValidDNG())
{
imgdata.process_warnings |= LIBRAW_WARN_DNG_NOT_PARSED;
return 0;
}
negative->Parse(*host, stream, info);
negative->PostParse(*host, stream, info);
int ifdindex = -1;
dng_ifd *rawIFD = search_for_ifd(info, libraw_internal_data.unpacker_data.data_offset, imgdata.sizes.raw_width,
imgdata.sizes.raw_height, ifdindex, stream);
if (rawIFD && ifdindex >= 0 && ifdindex == info.fMainIndex)
return 1;
if (rawIFD && ifdindex >= 0 && (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_ADD_PREVIEWS))
return 1;
return 0;
}
catch (...)
{
imgdata.process_warnings |= LIBRAW_WARN_DNG_NOT_PARSED;
return 0;
}
}
#ifdef USE_GPRSDK
if (libraw_internal_data.unpacker_data.tiff_compress == 9) // regardless of flags or use_dngsdk value!
return 1;
#endif
if (!imgdata.rawparams.use_dngsdk)
return 0;
if (load_raw == &LibRaw::lossy_dng_load_raw) // WHY??
return 0;
if (is_floating_point() && (imgdata.rawparams.use_dngsdk & LIBRAW_DNG_FLOAT))
return 1;
if (!imgdata.idata.filters && (imgdata.rawparams.use_dngsdk & LIBRAW_DNG_LINEAR))
return 1;
if (libraw_internal_data.unpacker_data.tiff_bps == 8 &&
(imgdata.rawparams.use_dngsdk & LIBRAW_DNG_8BIT))
return 1;
if (libraw_internal_data.unpacker_data.tiff_compress == 8 &&
(imgdata.rawparams.use_dngsdk & LIBRAW_DNG_DEFLATE))
return 1;
if (libraw_internal_data.unpacker_data.tiff_samples == 2)
return 0; // Always deny 2-samples (old fuji superccd)
if (imgdata.idata.filters == 9 &&
(imgdata.rawparams.use_dngsdk & LIBRAW_DNG_XTRANS))
return 1;
if (is_fuji_rotated())
return 0; // refuse
if (imgdata.rawparams.use_dngsdk & LIBRAW_DNG_OTHER)
return 1;
return 0;
#endif
}
int LibRaw::try_dngsdk()
{
#ifdef USE_DNGSDK
if (!dnghost)
return LIBRAW_UNSPECIFIED_ERROR;
dng_host *host = static_cast<dng_host *>(dnghost);
try
{
libraw_dng_stream stream(libraw_internal_data.internal_data.input);
AutoPtr<dng_negative> negative;
negative.Reset(host->Make_dng_negative());
dng_info info;
info.Parse(*host, stream);
info.PostParse(*host);
if (!info.IsValidDNG())
{
return LIBRAW_DATA_ERROR;
}
negative->Parse(*host, stream, info);
negative->PostParse(*host, stream, info);
int ifdindex;
dng_ifd *rawIFD = search_for_ifd(info,libraw_internal_data.unpacker_data.data_offset,imgdata.sizes.raw_width,imgdata.sizes.raw_height,ifdindex,stream);
if (!rawIFD)
{
imgdata.process_warnings |= LIBRAW_WARN_DNG_NOT_PROCESSED;
return LIBRAW_DATA_ERROR;
}
AutoPtr<dng_simple_image> stage2;
unsigned stageBits = 0; // 1=> release Stage2, 2=> change Black/Max
bool zerocopy = false;
bool isLossy = (rawIFD->fCompression == 34892) || (rawIFD->fCompression == 52546);
INT64 rawbytes = INT64(rawIFD->Bounds().W()) * INT64(rawIFD->Bounds().H()) * INT64(rawIFD->fSamplesPerPixel) * INT64(TagTypeSize(rawIFD->PixelType()));
if(rawbytes > INT64(imgdata.rawparams.max_raw_memory_mb) * INT64(1024 * 1024))
return LIBRAW_TOO_BIG;
if (
#ifdef USE_GPRSDK
libraw_internal_data.unpacker_data.tiff_compress != 9 &&
#endif
(
((libraw_internal_data.unpacker_data.tiff_compress == 34892
&& libraw_internal_data.unpacker_data.tiff_bps == 8
&& libraw_internal_data.unpacker_data.tiff_samples == 3
&& load_raw == &LibRaw::lossy_dng_load_raw) // JPEG DNG or JPEG DNG RAW Preview
|| (imgdata.rawparams.options & (LIBRAW_RAWOPTIONS_DNG_STAGE2| LIBRAW_RAWOPTIONS_DNG_STAGE3))
|| ((tiff_ifd[ifdindex].dng_levels.parsedfields & (LIBRAW_DNGFM_OPCODE2| LIBRAW_DNGFM_OPCODE3))
&& (imgdata.rawparams.options & (LIBRAW_RAWOPTIONS_DNG_STAGE2_IFPRESENT | LIBRAW_RAWOPTIONS_DNG_STAGE3_IFPRESENT)))
|| ((tiff_ifd[ifdindex].dng_levels.parsedfields & (LIBRAW_DNGFM_OPCODE2| LIBRAW_DNGFM_OPCODE3))
&& isLossy && (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_STAGE23_IFPRESENT_JPGJXL))
)
&& ifdindex >= 0)
)
{
if (info.fMainIndex != ifdindex)
info.fMainIndex = ifdindex;
if (rawIFD->fNewSubFileType == 1) // Preview
{
dng_read_image reader;
AutoPtr<dng_image> copy2;
negative->ReadStage1Image(*host, stream, info); // Read image AND opcodes lists
copy2.Reset((dng_simple_image*) negative->Stage1Image());
host->ApplyOpcodeList(negative->OpcodeList1(), *negative,copy2);
stageBits = 1;
if ((imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_STAGE2)
|| ((tiff_ifd[ifdindex].dng_levels.parsedfields & LIBRAW_DNGFM_OPCODE2) && (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_STAGE2_IFPRESENT))
|| ((tiff_ifd[ifdindex].dng_levels.parsedfields & LIBRAW_DNGFM_OPCODE2) && isLossy && (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_STAGE23_IFPRESENT_JPGJXL))
)
{
host->ApplyOpcodeList(negative->OpcodeList2(), *negative, copy2);
stageBits |= 2;
}
stage2.Reset((dng_simple_image *)copy2.Get());
copy2.Release();
}
else
{
negative->ReadStage1Image(*host, stream, info);
negative->BuildStage2Image(*host);
imgdata.process_warnings |= LIBRAW_WARN_DNG_STAGE2_APPLIED;
if ((imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_STAGE3)
||
((tiff_ifd[ifdindex].dng_levels.parsedfields & LIBRAW_DNGFM_OPCODE3) &&
(imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_STAGE3_IFPRESENT))
||
((tiff_ifd[ifdindex].dng_levels.parsedfields & LIBRAW_DNGFM_OPCODE3) && isLossy &&
(imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_STAGE23_IFPRESENT_JPGJXL))
)
{
negative->BuildStage3Image(*host);
stage2.Reset((dng_simple_image*)negative->Stage3Image());
imgdata.process_warnings |= LIBRAW_WARN_DNG_STAGE3_APPLIED;
}
else
stage2.Reset((dng_simple_image*)negative->Stage2Image());
stageBits = 3;
}
}
else
{
stage2.Reset(new dng_simple_image(rawIFD->Bounds(), rawIFD->fSamplesPerPixel, rawIFD->PixelType(), host->Allocator()));
#ifdef USE_GPRSDK
if (libraw_internal_data.unpacker_data.tiff_compress == 9)
{
gpr_allocator allocator;
allocator.Alloc = ::malloc;
allocator.Free = ::free;
gpr_buffer_auto vc5_image_obj(allocator.Alloc, allocator.Free);
gpr_read_image reader(&vc5_image_obj);
reader.Read(*host, *rawIFD, stream, *stage2.Get(), NULL, NULL);
}
else
#endif
{
dng_read_image reader;
reader.Read(*host, *rawIFD, stream, *stage2.Get(), NULL, NULL);
}
}
if (stage2->Bounds().W() != S.raw_width ||
stage2->Bounds().H() != S.raw_height)
{
if (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNG_ALLOWSIZECHANGE)
{
S.raw_width = S.width = stage2->Bounds().W();
S.left_margin = 0;
S.raw_height = S.height = stage2->Bounds().H();
S.top_margin = 0;
}
else
{
stage2.Release(); // It holds copy to internal dngnegative
imgdata.process_warnings |= LIBRAW_WARN_DNG_NOT_PROCESSED;
return LIBRAW_DATA_ERROR;
}
}
if (stageBits & 2)
{
if (stage2->Planes() > 1)
{
imgdata.idata.filters = 0;
imgdata.idata.colors = stage2->Planes();
}
// reset BL and whitepoint
imgdata.color.black = 0;
memset(imgdata.color.cblack, 0, sizeof(imgdata.color.cblack));
memset(imgdata.color.linear_max, 0, sizeof(imgdata.color.linear_max));
imgdata.color.maximum = 0xffff;
}
int pplanes = stage2->Planes();
int ptype = stage2->PixelType();
dng_pixel_buffer buffer;
stage2->GetPixelBuffer(buffer);
int pixels = stage2->Bounds().H() * stage2->Bounds().W() * pplanes;
if (ptype == ttShort && !(stageBits & 1) && !is_curve_linear())
{
imgdata.rawdata.raw_alloc = malloc(pixels * TagTypeSize(ptype));
ushort *src = (ushort *)buffer.fData;
ushort *dst = (ushort *)imgdata.rawdata.raw_alloc;
for (int i = 0; i < pixels; i++)
dst[i] = imgdata.color.curve[src[i]];
S.raw_pitch = S.raw_width * pplanes * TagTypeSize(ptype);
}
else if (ptype == ttByte)
{
imgdata.rawdata.raw_alloc = malloc(pixels * TagTypeSize(ttShort));
unsigned char *src = (unsigned char *)buffer.fData;
ushort *dst = (ushort *)imgdata.rawdata.raw_alloc;
if (is_curve_linear())
{
memmove(dst, src, pixels * TagTypeSize(ptype));
}
else
{
for (int i = 0; i < pixels; i++)
dst[i] = imgdata.color.curve[src[i]];
}
S.raw_pitch = S.raw_width * pplanes * TagTypeSize(ttShort);
}
else
{
// Alloc
if ((imgdata.rawparams.options & LIBRAW_RAWOPTIONS_DNGSDK_ZEROCOPY) && !(stageBits & 1))
{
zerocopy = true;
}
else
{
imgdata.rawdata.raw_alloc = malloc(pixels * TagTypeSize(ptype));
memmove(imgdata.rawdata.raw_alloc, buffer.fData,
pixels * TagTypeSize(ptype));
}
S.raw_pitch = S.raw_width * pplanes * TagTypeSize(ptype);
}
if (stageBits & 1)
stage2.Release();
if ((ptype == ttFloat) && (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_CONVERTFLOAT_TO_INT))
zerocopy = true;
if (zerocopy)
{
switch (ptype)
{
case ttFloat:
if (pplanes == 1)
imgdata.rawdata.float_image = (float *)buffer.fData;
else if (pplanes == 3)
imgdata.rawdata.float3_image = (float(*)[3])buffer.fData;
else if (pplanes == 4)
imgdata.rawdata.float4_image = (float(*)[4])buffer.fData;
break;
case ttShort:
if (pplanes == 1)
imgdata.rawdata.raw_image = (ushort *)buffer.fData;
else if (pplanes == 3)
imgdata.rawdata.color3_image = (ushort(*)[3])buffer.fData;
else if (pplanes == 4)
imgdata.rawdata.color4_image = (ushort(*)[4])buffer.fData;
break;
default:
/* do nothing */
break;
}
}
else
{
switch (ptype)
{
case ttFloat:
if (pplanes == 1)
imgdata.rawdata.float_image = (float *)imgdata.rawdata.raw_alloc;
else if (pplanes == 3)
imgdata.rawdata.float3_image = (float(*)[3])imgdata.rawdata.raw_alloc;
else if (pplanes == 4)
imgdata.rawdata.float4_image = (float(*)[4])imgdata.rawdata.raw_alloc;
break;
case ttByte:
case ttShort:
if (pplanes == 1)
imgdata.rawdata.raw_image = (ushort *)imgdata.rawdata.raw_alloc;
else if (pplanes == 3)
imgdata.rawdata.color3_image =
(ushort(*)[3])imgdata.rawdata.raw_alloc;
else if (pplanes == 4)
imgdata.rawdata.color4_image =
(ushort(*)[4])imgdata.rawdata.raw_alloc;
break;
default:
/* do nothing */
break;
}
}
if ((ptype == ttFloat) && (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_CONVERTFLOAT_TO_INT))
{
convertFloatToInt();
zerocopy = false;
}
if (zerocopy)
{
dng_negative *stolen = negative.Release();
dngnegative = stolen;
dng_simple_image *simage = stage2.Release();
dngimage = simage;
}
}
catch (...)
{
imgdata.process_warnings |= LIBRAW_WARN_DNG_NOT_PROCESSED;
return LIBRAW_UNSPECIFIED_ERROR;
}
int ret = (dngnegative || imgdata.rawdata.raw_alloc) ? LIBRAW_SUCCESS : LIBRAW_UNSPECIFIED_ERROR;
if(ret != LIBRAW_SUCCESS)
imgdata.process_warnings |= LIBRAW_WARN_DNG_NOT_PROCESSED;
return ret;
#else
return LIBRAW_UNSPECIFIED_ERROR;
#endif
}
void LibRaw::set_dng_host(void *p)
{
#ifdef USE_DNGSDK
dnghost = p;
#endif
}