Binary Tips

From FM Plugin Wikipedia
Jump to: navigation, search

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