version = "$VERSION 1.06 {08/21/2018 03:03:16 PM} VERSION$" /*`Rotate, Flip, Crop, Convert and Resize images and also splits TIFF images Uses Windows Image Acquisition (WIA) Rich@autotraker.com Thanks to Bernard Mouille for his help in figuring out a workaround for a bug in dBASE that prevented stacking commands. Uses WIA to change image file types. Can convert "BMP|PNG|GIF|JPG|TIF" See examples for more details on using each function. 08/21/2018 - Added SplitTIFF() function Converts multi-frame TIFF file into indivisual files You can combine functions by adding the right filters and setting the parameters for that filter but for ease of creating a CC I have separated the filters out into separate functions. Caution writing new routines, due to a bug in dBASE, objects need to be spit up to indivisual objects instead of stacking them like you find in most examples. More information at https://stackoverflow.com/questions/50215950/howo-to-rotate-crop-scale-flip Example Convert: Converts an image format to another imageIn = "C:\Program Files (x86)\dBASE\Plus12\Bin\dBASE PLUS 12_Activation_198x530.bmp" imageOut = set("directory")+"\Test.jpg" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.imageOut = imageOut o.convert() o.getPropertiesIn() o.getPropertiesOut() ? o.fileExtensionIn ?? " "+ o.formatIDIn ?? " "+ o.sizeIn ?? " "+ o.fileExtensionIn ?? " "+ o.heightIn ?? " "+ o.widthIn sizeIn = o.sizeIn ? o.fileExtensionOut ?? " "+ o.formatIDOut ?? " "+ o.sizeOut ?? " "+ o.fileExtensionOut ?? " "+ o.heightOut ?? " "+ o.widthOut sizeOut = o.sizeOut release object o close proc WIA.cc ? "New file " + (100 - (sizeOut/sizeIn*100)) + "% Smaller" return Quick convert: imageIn = "C:\Program Files (x86)\dBASE\Plus12\Bin\dBASE PLUS 12_Activation_198x530.bmp" imageOut = set("directory")+"\Test.jpg" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.imageOut = imageOut o.convert() release object o close proc WIA.cc return Example resize: // Resize an image to a set size imageIn = "C:\Program Files (x86)\dBASE\Plus12\Bin\dBASE PLUS 12_Activation_198x530.bmp" imageOut = set("directory")+"\Test.bmp" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.imageOut = imageOut o.getPropertiesIn() ? o.fileExtensionIn ?? " "+ o.formatIDIn ?? " "+ o.sizeIn ?? " "+ o.fileExtensionIn ?? " "+ o.heightIn ?? " "+ o.widthIn sizeIn = o.sizeIn newWidth = 100 newHeight = o.heightIn/o.widthIn * 100 o.widthOut = newWidth o.heightOut = newHeight o.keepAspect = true o.resize() o.getPropertiesOut() ? o.fileExtensionOut ?? " "+ o.formatIDOut ?? " "+ o.sizeOut ?? " "+ o.fileExtensionOut ?? " "+ o.heightOut ?? " "+ o.widthOut sizeOut = o.sizeOut release object o close proc WIA.cc ? "New file " + (100 - (sizeOut/sizeIn*100)) + "% Smaller" return Example Rotate: // Rotaes an image 90, 180, or 270 degrees imageIn = "C:\Program Files (x86)\dBASE\Plus12\Bin\dBASE PLUS 12_Activation_198x530.bmp" imageOut = set("directory")+"\Test.bmp" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.imageOut = imageOut o.RotationAngle = 90 o.rotate() release object o close proc WIA.cc Flip Example: Flips an image Horizontally or Vertically imageIn = "C:\Program Files (x86)\dBASE\Plus12\Bin\dBASE PLUS 12_Activation_198x530.bmp" imageOut = set("directory")+"\Test.bmp" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.imageOut = imageOut o.FlipHorizontal = false o.FlipVertical = true o.flip() release object o close proc WIA.cc Crop Example: // Crops a image to a set size imageIn = "C:\Program Files (x86)\dBASE\Plus12\Bin\dBASE PLUS 12_Activation_198x530.bmp" imageOut = set("directory")+"\Test.bmp" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.imageOut = imageOut o.getPropertiesIn() heightIn = o.heightIn widthIn = o.widthIn o.CropLeft = widthIn / 4 o.CropTop = heightIn / 4 o.CropRight = widthIn / 4 o.CropBottom = heightIn / 4 o.crop() release object o close proc WIA.cc Combined example: // Converts a BMP to JPG and then resizes the image. imageIn = "C:\Program Files (x86)\dBASE\Plus12\Bin\dBASE PLUS 12_Activation_198x530.bmp" tempFile = set("directory")+"\temp.jpg" imageOut = set("directory")+"\final.jpg" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.imageOut = tempFile if o.convert() // default it to JPG o.imageIn = tempFile o.imageOut = imageOut o.widthOut = 99 o.heightOut = 265 o.keepAspect = true o.resize() endif if new file().exists(tempFile) new file().delete(tempFile) endif release object o close proc WIA.cc Example for splitTIFF: // Split a multi-frame TIFF image into indivisual images. imageIn = set("directory")+"\multipage_tif_example.tif" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.newFormat = "JPG" o.newQuality = 90 o.splitTIFF() aFiles = o.outFiles release object o close proc WIA.cc for i = 1 to aFiles.size ? aFiles[i] endfor return Example to just get image information: imageIn = set("directory")+"\multipage_tif_example.tif" set proc to WIA.cc additive o = new WIA() o.imageIn = imageIn o.getPropertiesIn() ? o.fileExtensionIn // File extension ? o.heightIn // Image height ? o.widthIn // Image width ? o.formatIDIn // Image type ? o.frameCountIn // Frames in a multi-frame image (TIFF) ? o.sizeIn // Size on disk release object o close proc WIA.cc // Return */ #include WIA.h class WIA of string custom this.imageIn = set("directory")+"\Test.png" this.imageOut = set("directory")+"\Test.jpg" this.overwrite = true // always overwrite output file if true // Properties for convert this.newFormat = "JPG" // if useExtention is false, you have to specify output format. this.useExtention = true // use output file extension to determine output format this.newQuality = 90 // not all output formats use quality. JPG does. this.newCompression = "LZW" // default // properties for resize this.heightOut = 530/2 // new height this.widthOut = 198/2 // new width this.keepAspect = false // keep aspect ratio // properties for rotate this.RotationAngle = 0 // Set the RotationAngle property to 90, 180, or 270 if you wish // to rotate, otherwise 0 [the default] // properties for crop this.CroptLeft = 0 // Set the Left property to the left margin (in pixels) // if you wish to crop along the left, otherwise 0 [the default] this.CropTop = 0 // Set the Top property to the top margin (in pixels) // if you wish to crop along the top, otherwise 0 [the default] this.CropRight = 0 // Set the Right property to the right margin (in pixels) // if you wish to crop along the right, otherwise 0 [the default] this.CropBottom = 0 // Set the Bottom property to the bottom margin (in pixels) // if you wish to crop along the bottom, otherwise 0 [the default] // properties for flip this.FlipHorizontal = false // FlipHorizontal - Set the FlipHorizontal property to True if you wish to flip // the image horizontally, otherwise False [the default] this.FlipVertical = false // Set the FlipVertical property to True if you wish to flip // the image vertically, otherwise False [the default] // properties for splitTIFF this.destFolder = set("directory") // folder to receive frame images this.outFiles = new array() // file names of saved images // You also can set newFormat, default "JPG" and newQuality, default 90 function getPropertiesIn // returns information on the imput file local img img = new OleAutoClient( "WIA.ImageFile" ) img.LoadFile(this.imageIn) this.fileExtensionIn = img.fileExtension this.heightIn = img.height this.widthIn = img.width this.formatIDIn = img.formatID this.frameCountIn = img.FrameCount this.sizeIn = new file().size( this.imageIn ) release object img return function getPropertiesOut // returns information on output file local img img = new OleAutoClient( "WIA.ImageFile" ) img.LoadFile( this.imageOut ) this.fileExtensionOut = img.fileExtension this.heightOut = img.height this.widthOut = img.width this.formatIDOut = img.formatID this.frameCountOut = img.FrameCount this.sizeOut = new file().size( this.imageOut ) release object img return function convert // converts "BMP|PNG|GIF|JPG|TIF" to another of these image types if new file().exists(this.imageOut) if not this.overwrite return false endif // delete the output file if one exists new file().delete(this.imageOut) endif local img,IP,zz,zz1,zz2,zz3,type,cExtIn,cExtOut cExtIn = this.imageIn.right(this.imageIn.length-this.imageIn.lastIndexOf(".")-1).toUpperCase() cExtOut = this.imageOut.right(this.imageOut.length-this.imageOut.lastIndexOf(".")-1).toUpperCase() if not cExtOut $ "BMP|PNG|GIF|JPG|TIF" msgbox("Output file isn't of a supported type") return false endif if not cExtIn $ "BMP|PNG|GIF|JPG|TIF" msgbox("Input file isn't of a supported type") return false endif if this.useExtention this.newFormat = cExtOut else this.newFormat = this.newFormat.toUpperCase() endif img = new OleAutoClient( "WIA.ImageFile" ) img.LoadFile( this.imageIn ) IP = new OleAutoClient( "WIA.ImageProcess" ) IP.Filters.Add( IP.FilterInfos( "Convert" ).FilterID ) zz = IP.Filters(1) zz1 = zz.Properties( "FormatID" ) zz2 = zz.Properties( "Quality" ) zz3 = zz.Properties( "Compression" ) if this.newFormat == "BMP" type = wiaFormatBMP elseif this.newFormat == "PNG" type = wiaFormatPNG elseif this.newFormat == "GIF" type = wiaFormatGIF elseif this.newFormat == "JPG" type = wiaFormatJPEG elseif this.newFormat == "TIF" type = wiaFormatTIFF else type = wiaFormatJPEG endif zz1.Value := type zz2.Value := this.newQuality zz3.Value := this.newCompression img = IP.Apply( img ) img.SaveFile( this.ImageOut ) release object zz1 release object zz2 release object zz release object IP release object img return (new file().exists(this.ImageOut)) function resize // Resizes image if new file().exists(this.imageOut) if not this.overwrite return false endif // delete the output file if one exists new file().delete(this.imageOut) endif local img,IP,zz,zz1,cExtIn,cExtOut cExtIn = this.imageIn.right(this.imageIn.length-this.imageIn.lastIndexOf(".")-1).toUpperCase() cExtOut = this.imageOut.right(this.imageOut.length-this.imageOut.lastIndexOf(".")-1).toUpperCase() if not cExtIn $ "BMP|PNG|GIF|JPG|TIF" msgbox("Output file isn't of a supported type") return false endif if not cExtIn == cExtOut msgbox("Output file isn't of the same type as input file") return false endif img = new OleAutoClient( "WIA.ImageFile" ) img.LoadFile( this.imageIn ) IP = new OleAutoClient( "WIA.ImageProcess" ) IP.Filters.Add( IP.FilterInfos( "Scale" ).FilterID ) zz = IP.Filters(1) zz1 = zz.Properties( "MaximumWidth" ) ; zz1.Value := this.widthOut zz1 = zz.Properties( "MaximumHeight" ) ; zz1.Value := this.heightOut zz1 = zz.Properties("PreserveAspectRatio") ; zz1.Value := this.keepAspect img = IP.Apply( img ) img.SaveFile( this.ImageOut ) release object zz1 release object zz release object IP release object img return (new file().exists(this.ImageOut)) function rotate // rotates an image if new file().exists(this.imageOut) if not this.overwrite return false endif // delete the output file if one exists new file().delete(this.imageOut) endif local img,IP,zz,zz1,cExtIn,cExtOut cExtIn = this.imageIn.right(this.imageIn.length-this.imageIn.lastIndexOf(".")-1).toUpperCase() cExtOut = this.imageOut.right(this.imageOut.length-this.imageOut.lastIndexOf(".")-1).toUpperCase() if not cExtIn $ "BMP|PNG|GIF|JPG|TIF" msgbox("Output file isn't of a supported type") return false endif if not cExtIn == cExtOut msgbox("Output file isn't of the same type as input file") return false endif img = new OleAutoClient( "WIA.ImageFile" ) img.LoadFile( this.imageIn ) IP = new OleAutoClient( "WIA.ImageProcess" ) IP.Filters.Add( IP.FilterInfos("RotateFlip").filterid ) zz = IP.Filters(1) zz1 = zz.Properties( "RotationAngle" ) ; zz1.Value := this.RotationAngle img = IP.Apply( img ) img.SaveFile( this.ImageOut ) release object zz1 release object zz release object IP release object img return (new file().exists(this.ImageOut)) function flip // flips an image Horizontally or Vertically if new file().exists(this.imageOut) if not this.overwrite return false endif // delete the output file if one exists new file().delete(this.imageOut) endif local img,IP,zz,zz1,cExtIn,cExtOut cExtIn = this.imageIn.right(this.imageIn.length-this.imageIn.lastIndexOf(".")-1).toUpperCase() cExtOut = this.imageOut.right(this.imageOut.length-this.imageOut.lastIndexOf(".")-1).toUpperCase() if not cExtIn $ "BMP|PNG|GIF|JPG|TIF" msgbox("Output file isn't of a supported type") return false endif if not cExtIn == cExtOut msgbox("Output file isn't of the same type as input file") return false endif img = new OleAutoClient( "WIA.ImageFile" ) img.LoadFile( this.imageIn ) IP = new OleAutoClient( "WIA.ImageProcess" ) IP.Filters.Add( IP.FilterInfos("RotateFlip").filterid ) zz = IP.Filters(1) zz1 = zz.Properties( "FlipHorizontal" ) ; zz1.Value := this.FlipHorizontal zz1 = zz.Properties( "FlipVertical" ) ; zz1.Value := this.FlipVertical img = IP.Apply( img ) img.SaveFile( this.ImageOut ) release object zz1 release object zz release object IP release object img return (new file().exists(this.ImageOut)) function crop // Crops an image to a set size if new file().exists(this.imageOut) if not this.overwrite return false endif // delete the output file if one exists new file().delete(this.imageOut) endif local img,IP,zz,zz1,cExtIn,cExtOut cExtIn = this.imageIn.right(this.imageIn.length-this.imageIn.lastIndexOf(".")-1).toUpperCase() cExtOut = this.imageOut.right(this.imageOut.length-this.imageOut.lastIndexOf(".")-1).toUpperCase() if not cExtIn $ "BMP|PNG|GIF|JPG|TIF" msgbox("Output file isn't of a supported type") return false endif if not cExtIn == cExtOut msgbox("Output file isn't of the same type as input file") return false endif img = new OleAutoClient( "WIA.ImageFile" ) img.LoadFile( this.imageIn ) IP = new OleAutoClient( "WIA.ImageProcess" ) IP.Filters.Add( IP.FilterInfos("Crop").filterid ) zz = IP.Filters(1) zz1 = zz.Properties( "Left" ) ; zz1.Value := this.CropLeft zz1 = zz.Properties( "Top" ) ; zz1.Value := this.CropTop zz1 = zz.Properties( "Right" ) ; zz1.Value := this.CropRight zz1 = zz.Properties( "Bottom" ) ; zz1.Value := this.CropBottom img = IP.Apply( img ) img.SaveFile( this.ImageOut ) release object zz1 release object zz release object IP release object img return (new file().exists(this.ImageOut)) function splitTIFF // Splits a multi-frame TIFF immage into indivisual images of types "BMP|PNG|GIF|JPG|TIF" local img,IP,zz,zz1,zz2,zz3,type,cExtIn,nImages,nImage,baseName,newName cExtIn = this.imageIn.right(this.imageIn.length-this.imageIn.lastIndexOf(".")-1).toUpperCase() if not cExtIn $ "TIF" msgbox("Input file isn't of a supported type") return false endif this.newFormat = this.newFormat.toUpperCase() img = new OleAutoClient( "WIA.ImageFile" ) img.LoadFile( this.imageIn ) nImages = img.FrameCount for nImage = 1 to nImages IP = new OleAutoClient( "WIA.ImageProcess" ) IP.Filters.Add( IP.FilterInfos( "Convert" ).FilterID ) zz = IP.Filters(1) zz1 = zz.Properties( "FormatID" ) zz2 = zz.Properties( "Quality" ) zz3 = zz.Properties( "Compression" ) if this.newFormat == "BMP" type = wiaFormatBMP elseif this.newFormat == "PNG" type = wiaFormatPNG elseif this.newFormat == "GIF" type = wiaFormatGIF elseif this.newFormat == "JPG" type = wiaFormatJPEG elseif this.newFormat == "TIF" type = wiaFormatTIFF else type = wiaFormatJPEG endif zz1.Value := type zz2.Value := this.newQuality zz3.Value := this.newCompression img = IP.Apply( img ) baseName = class::getFileBaseName(this.imageIn) newName = this.destFolder+"\"+baseName+nImage+"."+this.newFormat if new file().exists(newName) new file().delete(newName) endif img.SaveFile( newName ) this.outFiles.add( newName ) endfor release object zz1 release object zz2 release object zz release object IP release object img return function getFileBaseName // returns base file name with both path and extension removed parameters inName local nPos nPos = inName.lastIndexof("\")+1 if nPos > 0 inName = inName.right(inName.length-nPos) endif nPos= inName.lastIndexof(".")+1 if nPos > 0 inName = inName.left(nPos-1) endif return inName endclass