| /* -*- 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 |
| } |