Difference between revisions of "Binary Tips"

From FM Plugin Wikipedia
Jump to: navigation, search
(Example Binary Functions)
(Example Image in / out)
 
(12 intermediate revisions by the same user not shown)
Line 38: Line 38:
  
  
=Example Binary Functions=
+
=Example Image in / out=
 +
 
 +
This example code takes an image as the first parameter - then loops through all 'streams' and then returns only the JPEG or GIF stream (with it's size data) - in effect removing any other data streams.
 +
This can be useful to reduce file size - but removing any unnecessary image data if all you want is to display the image in FileMaker
 +
 
 +
FMX_PROC(fmx::errcode) Do_Graphics_ImageOnly(short /* funcId */, const fmx::ExprEnv& /* environment */, const fmx::DataVect& dataVect, fmx::Data& results )
 +
{
 +
fmx::errcode err = -1;
 +
fmx::QuadCharAutoPtr bufType;
 +
fmx::BinaryDataAutoPtr out;
 +
 +
void * buffer;
 +
long item, count, size;
 +
short w = 0, h = 0;
 +
bool haveImage = false;
 +
 +
fmx::uint32 nParams = dataVect.Size(); // get the number of Parameters the user has passed.
 +
 +
fmx::QuadCharAutoPtr jpeg('J', 'P', 'E', 'G');
 +
fmx::QuadCharAutoPtr gif('G', 'I', 'F', 'f');
 +
uint32 jpegMacType = jpeg->GetMacType();
 +
uint32 gifMacType = gif->GetMacType();
 +
 +
 +
if( nParams == 1 )
 +
{
 +
const fmx::BinaryData& g = dataVect.AtAsBinaryData( 0 ); // get the container details
 +
 +
count = g.GetCount();
 +
 +
err = g.GetSIZEData(w, h);
 +
 +
if( err == 0)
 +
{
 +
for(item = 0; item <= count; item++)
 +
{
 +
g.GetType(item, *bufType);
 +
uint32 bufMacType = bufType->GetMacType();
 +
 +
if( (bufMacType == jpegMacType) || (bufMacType == gifMacType)  ) // is this a JPEG or GIFf stream ?
 +
{
 +
size = g.GetSize( item );
 +
if( size > 0 )
 +
{
 +
 +
buffer = new void *[ size ];
 +
if( buffer != NULL ) // make sure the memory was allocated
 +
{
 +
err = g.GetData ( item, 0, size, buffer );
 +
 +
if (err == 0)
 +
{
 +
err = out->Add ( *bufType, size, buffer );
 +
haveImage = (err == 0);
 +
}
 +
 +
free( buffer );  // release the memory we just allocated
 +
}
 +
}
 +
}
 +
}
 +
}
 +
}
 +
 +
if( (haveImage) && (err == 0) )
 +
{
 +
err = out->AddSIZEData( w, h ); // all images must have a size specified
 +
if( err == 0) results.SetBinaryData ( *out );
 +
 +
} else {
 +
// No image, handle appropriately
 +
}
 +
 +
 +
return err;
 +
 +
}
 +
 
 +
=Example Image Creation=
  
  
 
The following example code shows how to create a bitmap image, convert it to PNG and return to FileMaker
 
The following example code shows how to create a bitmap image, convert it to PNG and return to FileMaker
 
  
  
Line 58: Line 135:
 
    
 
    
 
  long size = 0;
 
  long size = 0;
  unsigned char * outBuffer = returnGraphicImage(pixelsWide, pixelsHigh, &size);
+
  unsigned char * outBuffer = returnGraphicImage(pixelsWide, pixelsHigh, &size); // Get our image
 
  if( outBuffer != NULL )
 
  if( outBuffer != NULL )
 
  {
 
  {
 
 
 
 
  err = outImage->Add ( *png, size, outBuffer );
+
  err = outImage->Add ( *png, size, outBuffer ); // Let FM know the image type is PNG
 
  if (err == 0)
 
  if (err == 0)
 
  {
 
  {
Line 68: Line 145:
 
  temp->Assign("Picture.png");
 
  temp->Assign("Picture.png");
 
  const fmx::Text&  name = *temp;
 
  const fmx::Text&  name = *temp;
  err = outImage->AddFNAMData( name ); // so FileMaker has a name for our image if the user tries to save it or external storage is used
+
  err = outImage->AddFNAMData( name ); // so FileMaker has a name for our image if the user tries to save it or external storage is used
 
 
 
 
 
  err = outImage->AddSIZEData( pixelsWide, pixelsHigh ); // all images must have a size specified
 
  err = outImage->AddSIZEData( pixelsWide, pixelsHigh ); // all images must have a size specified
 
  haveImage = ( err == 0 );
 
  haveImage = ( err == 0 );
  if( haveImage ) results.SetBinaryData ( *outImage );
+
  if( haveImage ) results.SetBinaryData ( *outImage ); // Add the image data
 
  }
 
  }
 
 
 
 
Line 80: Line 157:
 
  return err;
 
  return err;
 
  }
 
  }
 +
 +
WARNING: No error checking is included in the code below !
  
  
  
 
The 'returnGraphicImage' function for Mac:
 
The 'returnGraphicImage' function for Mac:
 
  
 
  #if defined(FMX_MAC_TARGET)
 
  #if defined(FMX_MAC_TARGET)
 +
#include <ApplicationServices/ApplicationServices.h>
 +
 
  unsigned char * returnGraphicImage( long width, long height, long *outSize )
 
  unsigned char * returnGraphicImage( long width, long height, long *outSize )
 
  {
 
  {
Line 150: Line 230:
 
  *outSize = size; // return the size of our created buffer
 
  *outSize = size; // return the size of our created buffer
 
  return outBuffer; // and the buffer itself
 
  return outBuffer; // and the buffer itself
 +
}
 +
#endif
 +
 +
 +
 +
The 'returnGraphicImage' function for Windows:
 +
 +
#if defined(FMX_WIN_TARGET)
 +
#include <gdiplus.h>
 +
#include "wincodec.h"  // graphic libraries for Windows
 +
using namespace Gdiplus;
 +
 +
unsigned char * returnTimetableImage( long width, long height, long* outSize )
 +
{
 +
// -----------------------------------------------------------------------------------------------------------------------------
 +
// Use GDI to draw our original Bitmap image
 +
 +
Bitmap * outBM = new Bitmap(width, height, PixelFormat32bppARGB );
 +
Graphics * gr = Graphics::FromImage( outBM );
 +
 +
gr->Clear(Gdiplus::Color::Transparent);
 +
 +
SolidBrush sb(Gdiplus::Color::Red);
 +
gr->FillRectangle( &sb, 20,20, 100, 50 );
 +
 +
sb.SetColor(Gdiplus::Color::Green);
 +
gr->FillRectangle( &sb, 220,220, 80, 60 );
 +
 +
Pen pen(Color(Gdiplus::Color::Black), 5);
 +
gr->DrawRectangle( &pen, 30, 30, 120, 70 );
 +
 +
 +
FontFamily fontFamily(L"Verdana");
 +
Font font(&fontFamily, 12, FontStyleRegular, UnitPixel);
 +
PointF pointF(160, 200);
 +
sb.SetColor(Gdiplus::Color::Black);
 +
gr->DrawString(L"Hello", -1, &font, pointF, &sb);
 +
 +
 +
RectF rectF( 180, 250, 100, 100 );
 +
sb.SetColor(Color(127,0,0,255));
 +
gr->FillRectangle( &sb, rectF );
 +
gr->DrawRectangle(&pen, rectF );
 +
 +
sb.SetColor(Color(Gdiplus::Color::Black));
 +
gr->DrawString(L"This is lots of text", -1, &font, rectF, NULL, &sb);
 +
 +
 +
Gdiplus::Rect r;
 +
r.X = 0;
 +
r.Y = 0;
 +
r.Width = width;
 +
r.Height = height;
 +
 +
BitmapData * bitmapData = new BitmapData;
 +
outBM->LockBits( &r, ImageLockModeRead, PixelFormat32bppARGB, bitmapData );  // Lock the bitmapData - must release it after 'CreateBitmapFromMemory'
 +
 +
BYTE* pixels = (BYTE*)bitmapData->Scan0; // This is the Bitmap image raw data
 +
UINT stride = bitmapData->Stride; // This is the bytes per line. We need this for 'CreateBitmapFromMemory' below
 +
 +
 +
// -----------------------------------------------------------------------------------------------------------------------------
 +
HRESULT hr = CoInitialize(NULL);
 +
 +
IWICImagingFactory* pFactory = NULL;
 +
hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pFactory);
 +
 +
//Create the outgoing stream
 +
HGLOBAL hOutGlobal = GlobalAlloc( GMEM_SHARE | GMEM_MOVEABLE, 0 );
 +
IStream* pOutStream = NULL;
 +
hr = CreateStreamOnHGlobal( hOutGlobal, FALSE, &pOutStream );
 +
 +
//Create the encoder.
 +
IWICBitmapEncoder* pEncoder = NULL;
 +
hr = pFactory->CreateEncoder( GUID_ContainerFormatPng, NULL, &pEncoder );
 +
hr = pEncoder->Initialize( pOutStream, WICBitmapEncoderNoCache );
 +
 +
//Get frame to decode
 +
IWICBitmapSource* pFrameDecode = NULL;
 +
hr = pFactory->CreateBitmapFromMemory( width, height, GUID_WICPixelFormat32bppBGRA, stride, height * stride, pixels, (IWICBitmap**)&pFrameDecode );
 +
 +
//Create frame to encode into
 +
IWICBitmapFrameEncode* pFrameEncode = NULL;
 +
IPropertyBag2* pPropertyBag = NULL;
 +
hr = pEncoder->CreateNewFrame( &pFrameEncode, &pPropertyBag );
 +
hr = pFrameEncode->Initialize( pPropertyBag );
 +
 +
// Set the encoder's size, resolution, color palette, pixel format, etc now
 +
WICRect rect = {0, 0, 0, 0};
 +
rect.Width = width;
 +
rect.Height = height;
 +
hr = pFrameEncode->WriteSource( pFrameDecode, &rect );
 +
 +
hr = pFrameEncode->Commit();
 +
hr = pEncoder->Commit();
 +
pFrameEncode->Release();
 +
pFrameDecode->Release();
 +
 +
//Get the bytes from the outStream
 +
ULARGE_INTEGER Size;
 +
LARGE_INTEGER zero;
 +
zero.QuadPart = 0;
 +
 +
pOutStream->Seek( zero, STREAM_SEEK_END, &Size );
 +
ULONG encoded;
 +
ULongLongToULong( Size.QuadPart, &encoded);
 +
UINT imageSize = encoded;
 +
 +
pOutStream->Release();
 +
 +
LPVOID p2 = GlobalLock(hOutGlobal);
 +
void * imagePtr = malloc( encoded );
 +
memcpy( imagePtr, p2, encoded );
 +
 +
pEncoder->Release();
 +
 +
GlobalUnlock( hOutGlobal );
 +
GlobalFree( hOutGlobal );
 +
 +
pFactory->Release();
 +
 +
// Clean up our original Bitmap
 +
outBM->UnlockBits( bitmapData );
 +
delete bitmapData;
 +
delete outBM;
 +
delete gr;
 +
 +
 +
*outSize = imageSize;
 +
    return (unsigned char *)imagePtr;
 
  }
 
  }
 
  #endif
 
  #endif

Latest revision as of 19:13, 20 May 2013

This page is about editing and working with Binary data (Images, sound, etc) passed from FileMaker to your plugin.


Binary Public Functions






Example Image in / out

This example code takes an image as the first parameter - then loops through all 'streams' and then returns only the JPEG or GIF stream (with it's size data) - in effect removing any other data streams. This can be useful to reduce file size - but removing any unnecessary image data if all you want is to display the image in FileMaker

FMX_PROC(fmx::errcode) Do_Graphics_ImageOnly(short /* funcId */, const fmx::ExprEnv& /* environment */, const fmx::DataVect& dataVect, fmx::Data& results )
{
	fmx::errcode			err = -1;
	fmx::QuadCharAutoPtr	bufType;
	fmx::BinaryDataAutoPtr	out;

	void *					buffer;
	long					item, count, size;
	short					w = 0, h = 0;
	bool					haveImage = false;
	
	fmx::uint32 nParams = dataVect.Size();							// get the number of Parameters the user has passed.
	
	fmx::QuadCharAutoPtr	jpeg('J', 'P', 'E', 'G');
	fmx::QuadCharAutoPtr	gif('G', 'I', 'F', 'f');
	uint32 jpegMacType = jpeg->GetMacType();
	uint32 gifMacType = gif->GetMacType();
	
	
	if( nParams == 1 )
	{
		const	fmx::BinaryData&	g = dataVect.AtAsBinaryData( 0 ); // get the container details
		
		count = g.GetCount();
		
		err = g.GetSIZEData(w, h);
		
		if( err == 0)
		{
			for(item = 0; item <= count; item++)
			{
				g.GetType(item, *bufType);
				uint32 bufMacType = bufType->GetMacType();
				
				if( (bufMacType == jpegMacType) || (bufMacType == gifMacType)  )	// is this a JPEG or GIFf stream ?
				{
					size = g.GetSize( item );
					if( size > 0 )
					{
						
						buffer = new void *[ size ];
						if( buffer != NULL )			// make sure the memory was allocated
						{
							err = g.GetData ( item, 0, size, buffer );
							
							if (err == 0)
							{
								err = out->Add ( *bufType, size, buffer );
								haveImage = (err == 0);
							}
							
							free( buffer );  // release the memory we just allocated
						}
					}
				}
			}
		}
	}
	
	if( (haveImage) && (err == 0) )
	{
		err = out->AddSIZEData( w, h );			// all images must have a size specified
		if( err == 0) results.SetBinaryData ( *out );
		
	} else {
		// No image, handle appropriately
	}

	
	return err;
	
}

Example Image Creation

The following example code shows how to create a bitmap image, convert it to PNG and return to FileMaker


FMX_PROC(fmx::errcode) Do_Graphic(short /* funcId */, const fmx::ExprEnv& /* environment */, const fmx::DataVect& dataVect, fmx::Data&  results )
{
	
	fmx::errcode	err = 0;
	bool            haveImage = false;
	fmx::QuadCharAutoPtr	png('P', 'N', 'G', 'f');
	fmx::BinaryDataAutoPtr	outImage;

    
	long pixelsWide = 300;
	long pixelsHigh = 150;
 
	long size = 0;
	unsigned char *	outBuffer = returnGraphicImage(pixelsWide, pixelsHigh, &size);		// Get our image
	if( outBuffer != NULL )
	{
		
		err = outImage->Add ( *png, size, outBuffer );					// Let FM know the image type is PNG
		if (err == 0)
		{
			fmx::TextAutoPtr	temp;
			temp->Assign("Picture.png");
			const fmx::Text&  name	=	*temp;
			err = outImage->AddFNAMData( name );					// so FileMaker has a name for our image if the user tries to save it or external storage is used
			
			err = outImage->AddSIZEData( pixelsWide, pixelsHigh );			// all images must have a size specified
			haveImage = ( err == 0 );
			if( haveImage )	results.SetBinaryData ( *outImage );			// Add the image data
		}
		
		delete [] outBuffer;
	}

	return err;
}

WARNING: No error checking is included in the code below !


The 'returnGraphicImage' function for Mac:

#if defined(FMX_MAC_TARGET)
#include <ApplicationServices/ApplicationServices.h>

unsigned char * returnGraphicImage( long width, long height, long *outSize )
{
	#define BYTES_PER_PIXEL	4

	CFMutableDataRef	imgData = CFDataCreateMutable( kCFAllocatorDefault, 0);
	if( imgData == NULL )	return NULL;
	
	CGImageDestinationRef	destRef = CGImageDestinationCreateWithData(imgData, kUTTypePNG, 1, NULL);
	if( destRef == NULL )	{ CFRelease( imgData );	return NULL; }

	// create an image from the layer
	void	*buffer = malloc(width * height * BYTES_PER_PIXEL);
	if( buffer == NULL )	{ CFRelease( destRef );	CFRelease( imgData );	return NULL; }
	memset(&buffer, 0, sizeof(buffer));


	CGColorSpaceRef	colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
	CGContextRef		context = CGBitmapContextCreate (buffer, width, height, 8, width * BYTES_PER_PIXEL, colorSpace, kCGImageAlphaPremultipliedLast);
	CGColorSpaceRelease( colorSpace );

	if( context == NULL )	{ CFRelease( destRef );	CFRelease( imgData );	free( buffer );	return NULL; }
	

	// Open the image area for drawing
	CGContextSaveGState(context);
	CGRect	rect = CGRectMake(0, 0, width, height);						// RECT representing the area available for drawing to.
	// ---------------------------------------------------------------------------------------------------------------

	CGContextSetRGBStrokeColor(context, 1, 0, 0, 1);					// Draw a 'Red' frame around our image.
	CGContextStrokeRectWithWidth(context, rect, 6);

	CGContextSelectFont(context, "Verdana", 12, kCGEncodingMacRoman);			// Add some text to our image
	CGContextSetAllowsAntialiasing(context, false);
	CGContextSetCharacterSpacing(context, 1.0);
	CGContextSetTextDrawingMode(context, kCGTextFill);
	CGContextShowTextAtPoint(context, 50.0, 50.0, "Hello and Welcome", 17);


	// ---------------------------------------------------------------------------------------------------------------

	CGImageRef	image		= CGBitmapContextCreateImage(context);			// We have finished drawing the image
	CGContextRestoreGState(context);

 	CGImageDestinationAddImage(destRef, image, NULL);					// Convert the image to PNG
	CGImageDestinationFinalize(destRef);
	
	CGContextRelease(context);
	CFRelease(destRef);									// have finished with destRef - our image should now be in 'imgData'
	free(buffer);										// have finished with the image buffer now too

	unsigned char * outBuffer = NULL;
	long size = (long)CFDataGetLength(imgData);
	if( size > 0 )
	{
		outBuffer = new unsigned char [ size ];
		
		if( outBuffer != NULL )		CFDataGetBytes( imgData, CFRangeMake(0, size), outBuffer );
	}
	
	CFRelease(imgData);

	*outSize = size;	// return the size of our created buffer
	return outBuffer;	// and the buffer itself
}
#endif


The 'returnGraphicImage' function for Windows:

#if defined(FMX_WIN_TARGET)
#include <gdiplus.h>
#include "wincodec.h"  // graphic libraries for Windows
using namespace Gdiplus;

unsigned char * returnTimetableImage( long width, long height, long* outSize )
{
	// -----------------------------------------------------------------------------------------------------------------------------
	// Use GDI to draw our original Bitmap image

	Bitmap * outBM = new Bitmap(width, height, PixelFormat32bppARGB );
	Graphics * gr = Graphics::FromImage( outBM );
	
	gr->Clear(Gdiplus::Color::Transparent);

	SolidBrush sb(Gdiplus::Color::Red);
	gr->FillRectangle( &sb, 20,20, 100, 50 );

	sb.SetColor(Gdiplus::Color::Green);
	gr->FillRectangle( &sb, 220,220, 80, 60 );

	Pen pen(Color(Gdiplus::Color::Black), 5);
	gr->DrawRectangle( &pen, 30, 30, 120, 70 );


	FontFamily	fontFamily(L"Verdana");
	Font		font(&fontFamily, 12, FontStyleRegular, UnitPixel);
	PointF		pointF(160, 200);
	sb.SetColor(Gdiplus::Color::Black);
	gr->DrawString(L"Hello", -1, &font, pointF, &sb);


	RectF		rectF( 180, 250, 100, 100 );
	sb.SetColor(Color(127,0,0,255));
	gr->FillRectangle( &sb, rectF );
	gr->DrawRectangle(&pen, rectF );

	sb.SetColor(Color(Gdiplus::Color::Black));
	gr->DrawString(L"This is lots of text", -1, &font, rectF, NULL, &sb);


	Gdiplus::Rect r;
	r.X = 0;
	r.Y = 0;
	r.Width = width;
	r.Height = height;

	BitmapData * bitmapData = new BitmapData;
	outBM->LockBits( &r, ImageLockModeRead, PixelFormat32bppARGB, bitmapData );   		// Lock the bitmapData - must release it after 'CreateBitmapFromMemory'

	BYTE* pixels = (BYTE*)bitmapData->Scan0;						// This is the Bitmap image raw data
	UINT stride = bitmapData->Stride;							// This is the bytes per line. We need this for 'CreateBitmapFromMemory' below


	// -----------------------------------------------------------------------------------------------------------------------------
	HRESULT hr = CoInitialize(NULL);
	
	IWICImagingFactory* pFactory = NULL;
	hr = CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_IWICImagingFactory, (LPVOID*)&pFactory);

	//Create the outgoing stream
	HGLOBAL hOutGlobal = GlobalAlloc( GMEM_SHARE | GMEM_MOVEABLE, 0 );
	IStream* pOutStream = NULL;
	hr = CreateStreamOnHGlobal( hOutGlobal, FALSE, &pOutStream );

	//Create the encoder.
	IWICBitmapEncoder* pEncoder = NULL;
	hr = pFactory->CreateEncoder( GUID_ContainerFormatPng, NULL, &pEncoder );
	hr = pEncoder->Initialize( pOutStream, WICBitmapEncoderNoCache );

	//Get frame to decode
	IWICBitmapSource* pFrameDecode = NULL;
	hr = pFactory->CreateBitmapFromMemory( width, height, GUID_WICPixelFormat32bppBGRA, stride, height * stride, pixels, (IWICBitmap**)&pFrameDecode );

	//Create frame to encode into
	IWICBitmapFrameEncode* pFrameEncode = NULL;
	IPropertyBag2* pPropertyBag = NULL;
	hr = pEncoder->CreateNewFrame( &pFrameEncode, &pPropertyBag );
	hr = pFrameEncode->Initialize( pPropertyBag );

	// Set the encoder's size, resolution, color palette, pixel format, etc now
	WICRect rect = {0, 0, 0, 0};
	rect.Width = width;
	rect.Height = height;
	hr = pFrameEncode->WriteSource( pFrameDecode, &rect );

	hr = pFrameEncode->Commit();
	hr = pEncoder->Commit();
	pFrameEncode->Release();
	pFrameDecode->Release();

	//Get the bytes from the outStream
	ULARGE_INTEGER Size;
	LARGE_INTEGER zero;
	zero.QuadPart = 0;

	pOutStream->Seek( zero, STREAM_SEEK_END, &Size );
	ULONG encoded;
	ULongLongToULong( Size.QuadPart, &encoded);
	UINT imageSize = encoded;
		
	pOutStream->Release();

	LPVOID p2 = GlobalLock(hOutGlobal);
	void * imagePtr = malloc( encoded );
	memcpy( imagePtr, p2, encoded );

	pEncoder->Release();
		
	GlobalUnlock( hOutGlobal );
	GlobalFree( hOutGlobal );

	pFactory->Release();

	// Clean up our original Bitmap
	outBM->UnlockBits( bitmapData );
	delete bitmapData;
	delete outBM;
	delete gr;


	*outSize = imageSize;
    return (unsigned char *)imagePtr;
}
#endif