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