// Function to convert a multi-image imagedocument into a (flat) single image
// D. R. G. Mitchell, adminnospam@dmscripting.com (remove the nospam to make this work)
// version:20210223, v1.0, February 2021, www.dmscripting.com

// Pass in the image document which must contain at least two image. Status is a passed in
// variable which takes the value of 1 when flattening has occurred and 0 when an imagedocument
// containing only one image has been passed in.
// Excisemode takes the value of 1 or 0. When 1, it specifies
// to return the flattened image to the exterior bounds of the component images. 
// When set to 0 it will return the image to the full size of the base image


// Function to flatten an imagedocument (containing several images) into a single
// image.

image FlattenImageDocument(imagedocument imgdoc, number &status, number excisemode)
	{
		// Count the number of images and create a clone
		// of the base image
		
		number noimgs=imgdoc.imagedocumentcountimages()
				

		// Loop through all the images to ensure that none are selected to ensure that
		// the base image is index 0
		

		number i
		image thisimg
		
		for(i=0; i<noimgs; i++)
			{
				thisimg:=imgdoc.imagedocumentgetimage(i)
				imagedisplay imgdisp=thisimg.imagegetimagedisplay(0)
				imgdisp.componentsetselected(0)
			}


		// Work through all the images to determine the outer bounds - some images may run
		// outside the area of the base image. The padded image into which such images get 
		// pasted, must span those limits, since in a flat image, the image cannot run outside
		// the image bounds. In an imagedocument containing multiple images - images can 
		// run outside the bounds of the image.
		
		number top, left, bottom, right
		image baseimg:=imgdoc.imagedocumentgetimage(0)
		number basexsize, baseysize
		getsize(baseimg, basexsize, baseysize)
		number exttop=0, extleft=0
		number extbottom=0, extright=0
		
		// If the mode is to excise the full image
		if(excisemode==0) 
			{
				extbottom=baseysize
				extright=basexsize
			}
		
		for(i=1; i<noimgs; i++)
			{
				image thisimg:=imgdoc.imagedocumentgetimage(i)
				imagedisplay imgdisp=thisimg.imagegetimagedisplay(0)
				imgdisp.componentgetrect(top, left, bottom, right)
				
				if(left<extleft) extleft=left
				if(top<exttop) exttop=top
				if(bottom>extbottom) extbottom=bottom
				if(right>extright) extright=right
			}
		
		
		// Create a padded image of a suitable size to fully encompass all the images, some of
		// which may run outside the area of the base image (in the original imagedocument)
		
		number padxsize=extright-extleft
		number padysize=extbottom-exttop
		image pasteimg=realimage("", 4, padxsize, padysize)
		
		
		// If images hang off the left or top, create and offset to ensure
		// that in the (larger) padded image, this offset is taken care of.
		// eg if an image sits at extremeright coordinate 15x, then when
		// it is transposed to the (larger) padded image at 0x an offset of +15 will have been applied.
		
		number xoffset, yoffset
		if(extleft<0) xoffset=extleft*-1
		else xoffset=0
		if(exttop<0) yoffset=exttop*-1
		else yoffset=0
						
		
		// Trap for imagedocuments containing only a single image

		if(noimgs==1)  
			{
				status=0
				return thisimg
			}
		
				
		// Loop to copy the contents of each image to the paddedimg
		
		for(i=1; i<noimgs; i++)
			{
				// Get the image and thus the image displays and its position in the image
				// document
				
				image thisimg:=imgdoc.imagedocumentgetimage(i)
				imagedisplay imgdisp=thisimg.imagegetimagedisplay(0)
				imgdisp.componentgetrect(top, left, bottom, right)


				// Round coordinate positions
				
				top=round(top)
				left=round(left)
				bottom=round(bottom)
				right=round(right)
				
				
				// Create new coordinates to map into the larger padded image
				
				number newtop=round(top+yoffset)
				number newleft=round(left+xoffset)
				number newbottom=round(bottom+yoffset)
				number newright=round(right+xoffset)
			
				
				// Ensure that the image position is within the bounds of the new base image
				
				if(newleft<0) newleft=0
				if(newtop<0) newtop=0
				if(newbottom>padysize) newbottom=padysize
				if(newright>padxsize) newright=padxsize

		
				// Ensure that the size of the copied image and the region into which it
				// is pasted are the same
				
				number imgwidth=newright-newleft
				number imgheight=newbottom-newtop
				pasteimg[newtop, newleft, newbottom, newright]=thisimg[0,0,imgheight, imgwidth]
			}
		
		status=1
		
		// return the image
		
		excisemode=round(excisemode)
		if(excisemode<0) excisemode=0
		if(excisemode>1) excisemode=1
		
		if(excisemode==0) return pasteimg // return the entire image
		
		if(excisemode==1) // return the image to the outerbounds
			{
				image cutout=pasteimg[exttop+yoffset, extleft+xoffset, extbottom+yoffset, extright+xoffset]
				return cutout
			}
	}


// Main script

// Check that an image is displayed

number nodocs=countdocumentwindowsoftype(5)
if(nodocs<1) exit(0)


// Source the front-most image document

imagedocument imgdoc=getfrontimagedocument()
number noimgs=imgdoc.imagedocumentcountimages()
if(noimgs<2)
	{
		showalert("The front-most imagedocument contains only one image.",2)
		exit(0)
	}

image firstimg:=imgdoc.imagedocumentgetimage(1)// source the first image for contrast values
imagedisplay firstdisp=firstimg.imagegetimagedisplay(0)
number mincontrast, maxcontrast
firstdisp.imagedisplaygetcontrastlimits(mincontrast, maxcontrast)


number status
number excisemode=1 // 0 = entire image is returned; 1 is image to outer bounds of componet images
image flatimage:=FlattenImageDocument(imgdoc, status, excisemode)
string imgname=imgdoc.imagedocumentgetname()


// Display it

showimage(flatimage)
if(status==1) setname(flatimage, imgname+" (flattened)")
else showalert("The passed in image contains only one image, and has not been flattened.",2)


// Transfer the calibration and tags

image baseimg:=imgdoc.imagedocumentgetimage(0)
taggroup basetags=baseimg.imagegettaggroup()
taggroup flattags=flatimage.imagegettaggroup()

taggroupcopytagsfrom(flattags, basetags)
imagecopycalibrationfrom(flatimage, baseimg)


// Set the contrast limits to those of the first image in the imagedocument

imagedisplay flatdisp=flatimage.imagegetimagedisplay(0)
flatdisp.imagedisplaysetdoautosurvey(0)
flatdisp.imagedisplaysetcontrastlimits(mincontrast, maxcontrast)


