@@ -417,32 +417,86 @@ IsoMCanonIad1Box::IsoMCanonIad1Box(const AbstractIsoMBox& base)
417417 reserved3 = data.get <uint16_t >();
418418
419419 if (2 == ind) { // ind is 2 for big images
420- sensorLeftBorder = data.get <uint16_t >();
421- sensorTopBorder = data.get <uint16_t >();
422- sensorRightBorder = data.get <uint16_t >();
423- sensorBottomBorder = data.get <uint16_t >();
424- data.skipBytes (2 * sizeof (uint16_t ));
425- sensorBlackAreaLeft = data.get <uint16_t >();
426- data.skipBytes (4 * sizeof (uint16_t ));
427- sensorBlackAreaTop = data.get <uint16_t >();
420+ cropLeftOffset = data.get <uint16_t >();
421+ cropTopOffset = data.get <uint16_t >();
422+ cropRightOffset = data.get <uint16_t >();
423+ cropBottomOffset = data.get <uint16_t >();
424+
425+ leftOpticalBlackLeftOffset = data.get <uint16_t >();
426+ leftOpticalBlackTopOffset = data.get <uint16_t >();
427+ leftOpticalBlackRightOffset = data.get <uint16_t >();
428+ leftOpticalBlackBottomOffset = data.get <uint16_t >();
429+
430+ topOpticalBlackLeftOffset = data.get <uint16_t >();
431+ topOpticalBlackTopOffset = data.get <uint16_t >();
432+ topOpticalBlackRightOffset = data.get <uint16_t >();
433+ topOpticalBlackBottomOffset = data.get <uint16_t >();
434+
435+ activeAreaLeftOffset = data.get <uint16_t >();
436+ activeAreaTopOffset = data.get <uint16_t >();
437+ activeAreaRightOffset = data.get <uint16_t >();
438+ activeAreaBottomOffset = data.get <uint16_t >();
439+ } else {
440+ // We hit a small image box?!
441+ ThrowRDE (" IAD1 box contains small image information, but big image expected" );
428442 }
429443
430444 writeLog (DEBUG_PRIO_EXTRA,
431445 " IAD1 sensor width: %d, height: %d, crop: %u, %u, %u, %u, black "
432446 " area left: %u, top: %u" ,
433- sensorWidth, sensorHeight, sensorLeftBorder, sensorTopBorder ,
434- sensorRightBorder, sensorBottomBorder, sensorBlackAreaLeft ,
435- sensorBlackAreaTop );
447+ sensorWidth, sensorHeight, cropLeftOffset, cropTopOffset ,
448+ cropRightOffset, cropBottomOffset, leftOpticalBlackRightOffset ,
449+ topOpticalBlackBottomOffset );
436450
437451 // Validate.
438452 operator bool ();
439453}
440454
441455IsoMCanonIad1Box::operator bool () const {
442- // No fields yet to validate, IAD1 is unused for decoding.
456+ if (!sensorWidth || !sensorHeight)
457+ ThrowIPE (" IAD1 sensor size unknown" );
458+ if (!cropRect ().isThisInside (sensorRect ()))
459+ ThrowIPE (" IAD1 crop rect is outside sensor rect" );
443460 return true ; // OK!
444461}
445462
463+ iRectangle2D IsoMCanonIad1Box::sensorRect () const {
464+ return iRectangle2D (0 , 0 , sensorWidth, sensorHeight);
465+ }
466+
467+ iRectangle2D IsoMCanonIad1Box::cropRect () const {
468+ return iRectangle2D (
469+ cropLeftOffset,
470+ cropTopOffset,
471+ (cropRightOffset+1 )-cropLeftOffset,
472+ (cropBottomOffset+1 )-cropTopOffset);
473+ }
474+
475+ iRectangle2D IsoMCanonIad1Box::leftOpticalBlackRect () const {
476+ return iRectangle2D (
477+ leftOpticalBlackLeftOffset,
478+ leftOpticalBlackTopOffset,
479+ (leftOpticalBlackRightOffset+1 )-leftOpticalBlackLeftOffset,
480+ (leftOpticalBlackBottomOffset+1 )-leftOpticalBlackTopOffset);
481+ }
482+
483+ iRectangle2D IsoMCanonIad1Box::topOpticalBlackRect () const {
484+ return iRectangle2D (
485+ topOpticalBlackLeftOffset,
486+ topOpticalBlackTopOffset,
487+ (topOpticalBlackRightOffset+1 )-topOpticalBlackLeftOffset,
488+ (topOpticalBlackBottomOffset+1 )-topOpticalBlackTopOffset);
489+ }
490+
491+ iRectangle2D IsoMCanonIad1Box::activeArea () const {
492+ return iRectangle2D (
493+ activeAreaLeftOffset,
494+ activeAreaTopOffset,
495+ (activeAreaRightOffset+1 )-activeAreaLeftOffset,
496+ (activeAreaBottomOffset+1 )-activeAreaTopOffset);
497+ }
498+
499+
446500CanonTimedMetadata::CanonTimedMetadata::Record::Record (ByteStream* bs) {
447501 assert (bs->getByteOrder () == Endianness::little);
448502 auto origPos = bs->getPosition ();
@@ -634,23 +688,34 @@ void Cr3Decoder::decodeMetaDataInternal(const CameraMetaData* meta) {
634688 }
635689
636690 setMetaData (meta, camId.make , camId.model , mode, iso);
691+ writeLog (DEBUG_PRIO_EXTRA, " blacklevel for ISO %d is %d" , mRaw ->metadata .isoSpeed , mRaw ->blackLevel );
637692
638- // IAD1 described sensor constraints
693+ // IAD1 describes sensor constraints
639694 const auto & iad1 = crawBox->CDI1 ()->IAD1 ();
640695
641696 if (mRaw ->blackAreas .empty ()) {
642- mRaw ->blackAreas .push_back (BlackArea (0 , iad1->sensorBlackAreaLeft , true ));
643- mRaw ->blackAreas .push_back (BlackArea (0 , iad1->sensorBlackAreaTop , false ));
697+ // IAD1 stores the rectangles for black areas.
698+ auto leftOpticalBlack = iad1->leftOpticalBlackRect ();
699+ auto topOpticalBlack = iad1->topOpticalBlackRect ();
700+ if (leftOpticalBlack.dim .x >= 12 +4 ) {
701+ // if left optical black has >= 12+4 pixels, we reduce them by 12 as some
702+ // models (EOS RP is known) has white pixels in this area.
703+ // Yes, this is hacky, but IAD1 reports offset=0 which is either wrong or the white pixels
704+ // are a camera bug and must be resolved in software.
705+ leftOpticalBlack.pos .x += 12 ;
706+ leftOpticalBlack.dim .x -= 12 ;
707+ }
708+ if (topOpticalBlack.dim .y >= 12 +4 ) {
709+ // Same must be done for horizontal pixels
710+ topOpticalBlack.pos .y += 12 ;
711+ topOpticalBlack.dim .y -= 12 ;
712+ }
713+ mRaw ->blackAreas .push_back (BlackArea (leftOpticalBlack.pos .x , leftOpticalBlack.dim .x , true ));
714+ mRaw ->blackAreas .push_back (BlackArea (topOpticalBlack.pos .y , topOpticalBlack.pos .y , false ));
644715 }
645716
646717 if (applyCrop) {
647- // IAD1 sensor parameters always crop one more pixel as needed.
648- // We add back the missing pixels to match official specifications.
649- iRectangle2D new_area (iad1->sensorLeftBorder , iad1->sensorTopBorder ,
650- iad1->sensorRightBorder - iad1->sensorLeftBorder + 1 ,
651- iad1->sensorBottomBorder - iad1->sensorTopBorder + 1 );
652-
653- mRaw ->subFrame (new_area);
718+ mRaw ->subFrame (iad1->cropRect ());
654719 }
655720}
656721
0 commit comments