You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
849 lines
16 KiB
849 lines
16 KiB
/*****************************************************************************/
|
|
// Copyright 2006-2008 Adobe Systems Incorporated
|
|
// All Rights Reserved.
|
|
//
|
|
// NOTICE: Adobe permits you to use, modify, and distribute this file in
|
|
// accordance with the terms of the Adobe license agreement accompanying it.
|
|
/*****************************************************************************/
|
|
|
|
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_image.cpp#1 $ */
|
|
/* $DateTime: 2012/05/30 13:28:51 $ */
|
|
/* $Change: 832332 $ */
|
|
/* $Author: tknoll $ */
|
|
|
|
/*****************************************************************************/
|
|
|
|
#include "dng_image.h"
|
|
|
|
#include "dng_assertions.h"
|
|
#include "dng_exceptions.h"
|
|
#include "dng_orientation.h"
|
|
#include "dng_pixel_buffer.h"
|
|
#include "dng_tag_types.h"
|
|
#include "dng_tile_iterator.h"
|
|
#include "dng_utils.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_tile_buffer::dng_tile_buffer (const dng_image &image,
|
|
const dng_rect &tile,
|
|
bool dirty)
|
|
|
|
: fImage (image)
|
|
, fRefData (NULL)
|
|
|
|
{
|
|
|
|
fImage.AcquireTileBuffer (*this,
|
|
tile,
|
|
dirty);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_tile_buffer::~dng_tile_buffer ()
|
|
{
|
|
|
|
fImage.ReleaseTileBuffer (*this);
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_const_tile_buffer::dng_const_tile_buffer (const dng_image &image,
|
|
const dng_rect &tile)
|
|
|
|
: dng_tile_buffer (image, tile, false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_const_tile_buffer::~dng_const_tile_buffer ()
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_dirty_tile_buffer::dng_dirty_tile_buffer (dng_image &image,
|
|
const dng_rect &tile)
|
|
|
|
: dng_tile_buffer (image, tile, true)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_dirty_tile_buffer::~dng_dirty_tile_buffer ()
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_image::dng_image (const dng_rect &bounds,
|
|
uint32 planes,
|
|
uint32 pixelType)
|
|
|
|
: fBounds (bounds)
|
|
, fPlanes (planes)
|
|
, fPixelType (pixelType)
|
|
|
|
{
|
|
|
|
if (bounds.IsEmpty () || planes == 0 || PixelSize () == 0)
|
|
{
|
|
|
|
#if qDNGValidate
|
|
|
|
ReportError ("Fuzz: Attempt to create zero size image");
|
|
|
|
#endif
|
|
|
|
ThrowBadFormat ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_image::~dng_image ()
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_image * dng_image::Clone () const
|
|
{
|
|
|
|
ThrowProgramError ("Clone is not supported by this dng_image subclass");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::SetPixelType (uint32 pixelType)
|
|
{
|
|
|
|
if (TagTypeSize (pixelType) != PixelSize ())
|
|
{
|
|
|
|
ThrowProgramError ("Cannot change pixel size for existing image");
|
|
|
|
}
|
|
|
|
fPixelType = pixelType;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_image::PixelSize () const
|
|
{
|
|
|
|
return TagTypeSize (PixelType ());
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
uint32 dng_image::PixelRange () const
|
|
{
|
|
|
|
switch (fPixelType)
|
|
{
|
|
|
|
case ttByte:
|
|
case ttSByte:
|
|
{
|
|
return 0x0FF;
|
|
}
|
|
|
|
case ttShort:
|
|
case ttSShort:
|
|
{
|
|
return 0x0FFFF;
|
|
}
|
|
|
|
case ttLong:
|
|
case ttSLong:
|
|
{
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
dng_rect dng_image::RepeatingTile () const
|
|
{
|
|
|
|
return fBounds;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::AcquireTileBuffer (dng_tile_buffer & /* buffer */,
|
|
const dng_rect & /* area */,
|
|
bool /* dirty */) const
|
|
{
|
|
|
|
ThrowProgramError ();
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::ReleaseTileBuffer (dng_tile_buffer & /* buffer */) const
|
|
{
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::DoGet (dng_pixel_buffer &buffer) const
|
|
{
|
|
|
|
dng_rect tile;
|
|
|
|
dng_tile_iterator iter (*this, buffer.fArea);
|
|
|
|
while (iter.GetOneTile (tile))
|
|
{
|
|
|
|
dng_const_tile_buffer tileBuffer (*this, tile);
|
|
|
|
buffer.CopyArea (tileBuffer,
|
|
tile,
|
|
buffer.fPlane,
|
|
buffer.fPlanes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::DoPut (const dng_pixel_buffer &buffer)
|
|
{
|
|
|
|
dng_rect tile;
|
|
|
|
dng_tile_iterator iter (*this, buffer.fArea);
|
|
|
|
while (iter.GetOneTile (tile))
|
|
{
|
|
|
|
dng_dirty_tile_buffer tileBuffer (*this, tile);
|
|
|
|
tileBuffer.CopyArea (buffer,
|
|
tile,
|
|
buffer.fPlane,
|
|
buffer.fPlanes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::GetRepeat (dng_pixel_buffer &buffer,
|
|
const dng_rect &srcArea,
|
|
const dng_rect &dstArea) const
|
|
{
|
|
|
|
// If we already have the entire srcArea in the
|
|
// buffer, we can just repeat that.
|
|
|
|
if ((srcArea & buffer.fArea) == srcArea)
|
|
{
|
|
|
|
buffer.RepeatArea (srcArea,
|
|
dstArea);
|
|
|
|
}
|
|
|
|
// Else we first need to get the srcArea into the buffer area.
|
|
|
|
else
|
|
{
|
|
|
|
// Find repeating pattern size.
|
|
|
|
dng_point repeat = srcArea.Size ();
|
|
|
|
// Find pattern phase at top-left corner of destination area.
|
|
|
|
dng_point phase = dng_pixel_buffer::RepeatPhase (srcArea,
|
|
dstArea);
|
|
|
|
// Find new source area at top-left of dstArea.
|
|
|
|
dng_rect newArea = srcArea + (dstArea.TL () -
|
|
srcArea.TL ());
|
|
|
|
// Find quadrant split coordinates.
|
|
|
|
int32 splitV = newArea.t + repeat.v - phase.v;
|
|
int32 splitH = newArea.l + repeat.h - phase.h;
|
|
|
|
// Top-left quadrant.
|
|
|
|
dng_rect dst1 (dng_rect (newArea.t,
|
|
newArea.l,
|
|
splitV,
|
|
splitH) & dstArea);
|
|
|
|
if (dst1.NotEmpty ())
|
|
{
|
|
|
|
dng_pixel_buffer temp (buffer);
|
|
|
|
temp.fArea = dst1 + (srcArea.TL () -
|
|
dstArea.TL () +
|
|
dng_point (phase.v, phase.h));
|
|
|
|
temp.fData = buffer.DirtyPixel (dst1.t,
|
|
dst1.l,
|
|
buffer.fPlane);
|
|
|
|
DoGet (temp);
|
|
|
|
}
|
|
|
|
// Top-right quadrant.
|
|
|
|
dng_rect dst2 (dng_rect (newArea.t,
|
|
splitH,
|
|
splitV,
|
|
newArea.r) & dstArea);
|
|
|
|
if (dst2.NotEmpty ())
|
|
{
|
|
|
|
dng_pixel_buffer temp (buffer);
|
|
|
|
temp.fArea = dst2 + (srcArea.TL () -
|
|
dstArea.TL () +
|
|
dng_point (phase.v, -phase.h));
|
|
|
|
temp.fData = buffer.DirtyPixel (dst2.t,
|
|
dst2.l,
|
|
buffer.fPlane);
|
|
|
|
DoGet (temp);
|
|
|
|
}
|
|
|
|
// Bottom-left quadrant.
|
|
|
|
dng_rect dst3 (dng_rect (splitV,
|
|
newArea.l,
|
|
newArea.b,
|
|
splitH) & dstArea);
|
|
|
|
if (dst3.NotEmpty ())
|
|
{
|
|
|
|
dng_pixel_buffer temp (buffer);
|
|
|
|
temp.fArea = dst3 + (srcArea.TL () -
|
|
dstArea.TL () +
|
|
dng_point (-phase.v, phase.h));
|
|
|
|
temp.fData = buffer.DirtyPixel (dst3.t,
|
|
dst3.l,
|
|
buffer.fPlane);
|
|
|
|
DoGet (temp);
|
|
|
|
}
|
|
|
|
// Bottom-right quadrant.
|
|
|
|
dng_rect dst4 (dng_rect (splitV,
|
|
splitH,
|
|
newArea.b,
|
|
newArea.r) & dstArea);
|
|
|
|
if (dst4.NotEmpty ())
|
|
{
|
|
|
|
dng_pixel_buffer temp (buffer);
|
|
|
|
temp.fArea = dst4 + (srcArea.TL () -
|
|
dstArea.TL () +
|
|
dng_point (-phase.v, -phase.h));
|
|
|
|
temp.fData = buffer.DirtyPixel (dst4.t,
|
|
dst4.l,
|
|
buffer.fPlane);
|
|
|
|
DoGet (temp);
|
|
|
|
}
|
|
|
|
// Replicate this new source area.
|
|
|
|
buffer.RepeatArea (newArea,
|
|
dstArea);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::GetEdge (dng_pixel_buffer &buffer,
|
|
edge_option edgeOption,
|
|
const dng_rect &srcArea,
|
|
const dng_rect &dstArea) const
|
|
{
|
|
|
|
switch (edgeOption)
|
|
{
|
|
|
|
case edge_zero:
|
|
{
|
|
|
|
buffer.SetZero (dstArea,
|
|
buffer.fPlane,
|
|
buffer.fPlanes);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case edge_repeat:
|
|
{
|
|
|
|
GetRepeat (buffer,
|
|
srcArea,
|
|
dstArea);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case edge_repeat_zero_last:
|
|
{
|
|
|
|
if (buffer.fPlanes > 1)
|
|
{
|
|
|
|
dng_pixel_buffer buffer1 (buffer);
|
|
|
|
buffer1.fPlanes--;
|
|
|
|
GetEdge (buffer1,
|
|
edge_repeat,
|
|
srcArea,
|
|
dstArea);
|
|
|
|
}
|
|
|
|
dng_pixel_buffer buffer2 (buffer);
|
|
|
|
buffer2.fPlane = buffer.fPlanes - 1;
|
|
buffer2.fPlanes = 1;
|
|
|
|
buffer2.fData = buffer.DirtyPixel (buffer2.fArea.t,
|
|
buffer2.fArea.l,
|
|
buffer2.fPlane);
|
|
|
|
GetEdge (buffer2,
|
|
edge_zero,
|
|
srcArea,
|
|
dstArea);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
{
|
|
|
|
ThrowProgramError ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::Get (dng_pixel_buffer &buffer,
|
|
edge_option edgeOption,
|
|
uint32 repeatV,
|
|
uint32 repeatH) const
|
|
{
|
|
|
|
// Find the overlap with the image bounds.
|
|
|
|
dng_rect overlap = buffer.fArea & fBounds;
|
|
|
|
// Move the overlapping pixels.
|
|
|
|
if (overlap.NotEmpty ())
|
|
{
|
|
|
|
dng_pixel_buffer temp (buffer);
|
|
|
|
temp.fArea = overlap;
|
|
|
|
temp.fData = buffer.DirtyPixel (overlap.t,
|
|
overlap.l,
|
|
buffer.fPlane);
|
|
|
|
DoGet (temp);
|
|
|
|
}
|
|
|
|
// See if we need to pad the edge values.
|
|
|
|
if ((edgeOption != edge_none) && (overlap != buffer.fArea))
|
|
{
|
|
|
|
dng_rect areaT (buffer.fArea);
|
|
dng_rect areaL (buffer.fArea);
|
|
dng_rect areaB (buffer.fArea);
|
|
dng_rect areaR (buffer.fArea);
|
|
|
|
areaT.b = Min_int32 (areaT.b, fBounds.t);
|
|
areaL.r = Min_int32 (areaL.r, fBounds.l);
|
|
areaB.t = Max_int32 (areaB.t, fBounds.b);
|
|
areaR.l = Max_int32 (areaR.l, fBounds.r);
|
|
|
|
dng_rect areaH (buffer.fArea);
|
|
dng_rect areaV (buffer.fArea);
|
|
|
|
areaH.l = Max_int32 (areaH.l, fBounds.l);
|
|
areaH.r = Min_int32 (areaH.r, fBounds.r);
|
|
|
|
areaV.t = Max_int32 (areaV.t, fBounds.t);
|
|
areaV.b = Min_int32 (areaV.b, fBounds.b);
|
|
|
|
// Top left.
|
|
|
|
dng_rect areaTL = areaT & areaL;
|
|
|
|
if (areaTL.NotEmpty ())
|
|
{
|
|
|
|
GetEdge (buffer,
|
|
edgeOption,
|
|
dng_rect (fBounds.t,
|
|
fBounds.l,
|
|
fBounds.t + (int32)repeatV,
|
|
fBounds.l + (int32)repeatH),
|
|
areaTL);
|
|
|
|
}
|
|
|
|
// Top middle.
|
|
|
|
dng_rect areaTM = areaT & areaH;
|
|
|
|
if (areaTM.NotEmpty ())
|
|
{
|
|
|
|
GetEdge (buffer,
|
|
edgeOption,
|
|
dng_rect (fBounds.t,
|
|
areaTM.l,
|
|
fBounds.t + (int32)repeatV,
|
|
areaTM.r),
|
|
areaTM);
|
|
|
|
}
|
|
|
|
// Top right.
|
|
|
|
dng_rect areaTR = areaT & areaR;
|
|
|
|
if (areaTR.NotEmpty ())
|
|
{
|
|
|
|
GetEdge (buffer,
|
|
edgeOption,
|
|
dng_rect (fBounds.t,
|
|
fBounds.r - (int32)repeatH,
|
|
fBounds.t + (int32)repeatV,
|
|
fBounds.r),
|
|
areaTR);
|
|
|
|
}
|
|
|
|
// Left middle.
|
|
|
|
dng_rect areaLM = areaL & areaV;
|
|
|
|
if (areaLM.NotEmpty ())
|
|
{
|
|
|
|
GetEdge (buffer,
|
|
edgeOption,
|
|
dng_rect (areaLM.t,
|
|
fBounds.l,
|
|
areaLM.b,
|
|
fBounds.l + (int32)repeatH),
|
|
areaLM);
|
|
|
|
}
|
|
|
|
// Right middle.
|
|
|
|
dng_rect areaRM = areaR & areaV;
|
|
|
|
if (areaRM.NotEmpty ())
|
|
{
|
|
|
|
GetEdge (buffer,
|
|
edgeOption,
|
|
dng_rect (areaRM.t,
|
|
fBounds.r - (int32)repeatH,
|
|
areaRM.b,
|
|
fBounds.r),
|
|
areaRM);
|
|
|
|
}
|
|
|
|
// Bottom left.
|
|
|
|
dng_rect areaBL = areaB & areaL;
|
|
|
|
if (areaBL.NotEmpty ())
|
|
{
|
|
|
|
GetEdge (buffer,
|
|
edgeOption,
|
|
dng_rect (fBounds.b - (int32)repeatV,
|
|
fBounds.l,
|
|
fBounds.b,
|
|
fBounds.l + (int32)repeatH),
|
|
areaBL);
|
|
|
|
}
|
|
|
|
// Bottom middle.
|
|
|
|
dng_rect areaBM = areaB & areaH;
|
|
|
|
if (areaBM.NotEmpty ())
|
|
{
|
|
|
|
GetEdge (buffer,
|
|
edgeOption,
|
|
dng_rect (fBounds.b - (int32)repeatV,
|
|
areaBM.l,
|
|
fBounds.b,
|
|
areaBM.r),
|
|
areaBM);
|
|
|
|
}
|
|
|
|
// Bottom right.
|
|
|
|
dng_rect areaBR = areaB & areaR;
|
|
|
|
if (areaBR.NotEmpty ())
|
|
{
|
|
|
|
GetEdge (buffer,
|
|
edgeOption,
|
|
dng_rect (fBounds.b - (int32)repeatV,
|
|
fBounds.r - (int32)repeatH,
|
|
fBounds.b,
|
|
fBounds.r),
|
|
areaBR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::Put (const dng_pixel_buffer &buffer)
|
|
{
|
|
|
|
// Move the overlapping pixels.
|
|
|
|
dng_rect overlap = buffer.fArea & fBounds;
|
|
|
|
if (overlap.NotEmpty ())
|
|
{
|
|
|
|
dng_pixel_buffer temp (buffer);
|
|
|
|
temp.fArea = overlap;
|
|
|
|
temp.fData = (void *) buffer.ConstPixel (overlap.t,
|
|
overlap.l,
|
|
buffer.fPlane);
|
|
|
|
// Move the overlapping planes.
|
|
|
|
if (temp.fPlane < Planes ())
|
|
{
|
|
|
|
temp.fPlanes = Min_uint32 (temp.fPlanes,
|
|
Planes () - temp.fPlane);
|
|
|
|
DoPut (temp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::Trim (const dng_rect &r)
|
|
{
|
|
|
|
if (r != Bounds ())
|
|
{
|
|
|
|
ThrowProgramError ("Trim is not support by this dng_image subclass");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::Rotate (const dng_orientation &orientation)
|
|
{
|
|
|
|
if (orientation != dng_orientation::Normal ())
|
|
{
|
|
|
|
ThrowProgramError ("Rotate is not support by this dng_image subclass");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::CopyArea (const dng_image &src,
|
|
const dng_rect &area,
|
|
uint32 srcPlane,
|
|
uint32 dstPlane,
|
|
uint32 planes)
|
|
{
|
|
|
|
if (&src == this)
|
|
return;
|
|
|
|
dng_tile_iterator destIter(*this, area);
|
|
dng_rect destTileArea;
|
|
|
|
while (destIter.GetOneTile(destTileArea))
|
|
{
|
|
dng_tile_iterator srcIter(src, destTileArea);
|
|
dng_rect srcTileArea;
|
|
|
|
while (srcIter.GetOneTile(srcTileArea))
|
|
{
|
|
|
|
dng_dirty_tile_buffer destTile(*this, srcTileArea);
|
|
dng_const_tile_buffer srcTile(src, srcTileArea);
|
|
|
|
destTile.CopyArea (srcTile, srcTileArea, srcPlane, dstPlane, planes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
bool dng_image::EqualArea (const dng_image &src,
|
|
const dng_rect &area,
|
|
uint32 plane,
|
|
uint32 planes) const
|
|
{
|
|
|
|
if (&src == this)
|
|
return true;
|
|
|
|
dng_tile_iterator destIter (*this, area);
|
|
|
|
dng_rect destTileArea;
|
|
|
|
while (destIter.GetOneTile (destTileArea))
|
|
{
|
|
|
|
dng_tile_iterator srcIter (src, destTileArea);
|
|
|
|
dng_rect srcTileArea;
|
|
|
|
while (srcIter.GetOneTile (srcTileArea))
|
|
{
|
|
|
|
dng_const_tile_buffer destTile (*this, srcTileArea);
|
|
dng_const_tile_buffer srcTile (src , srcTileArea);
|
|
|
|
if (!destTile.EqualArea (srcTile, srcTileArea, plane, planes))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void dng_image::SetConstant (uint32 value,
|
|
const dng_rect &area)
|
|
{
|
|
|
|
dng_tile_iterator iter (*this, area);
|
|
|
|
dng_rect tileArea;
|
|
|
|
while (iter.GetOneTile (tileArea))
|
|
{
|
|
|
|
dng_dirty_tile_buffer buffer (*this, tileArea);
|
|
|
|
buffer.SetConstant (tileArea,
|
|
0,
|
|
fPlanes,
|
|
value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*****************************************************************************/
|