Difference between revisions of "Binary Tips"
From FM Plugin Wikipedia
(→Example Image in / out) |
|||
(14 intermediate revisions by 2 users not shown) | |||
Line 7: | Line 7: | ||
=Binary Public Functions= | =Binary Public Functions= | ||
+ | *[[BinaryData &operator = ( const BinaryData &source )]] | ||
+ | *[[bool operator == ( const BinaryData &compareData ) const]] | ||
+ | *[[bool operator != ( const BinaryData &compareData ) const]] | ||
+ | *[[int GetCount () const]] | ||
+ | *[[int GetIndex ( const QuadChar& dataType ) const]] | ||
+ | *[[ulong GetTotalSize () const]] | ||
+ | *[[void GetType ( int index, QuadChar& dataType ) const]] | ||
+ | *[[ulong GetSize ( int index ) const]] | ||
+ | *[[errcode GetData ( int index, ulong offset, ulong amount, void *buffer ) const]] | ||
+ | |||
+ | *[[errcode Add ( const QuadChar& dataType, ulong amount, void *buffer )]] | ||
+ | *[[bool Remove ( const QuadChar& dataType )]] | ||
+ | *[[void RemoveAll ()]] | ||
− | =Example | + | *[[errcode GetFNAMData ( Text &filepathlist ) const]] |
+ | *[[errcode AddFNAMData ( const Text &filepathlist )]] | ||
+ | *[[errcode GetSIZEData ( short &width, short &height ) const]] | ||
+ | *[[errcode AddSIZEData ( short width, short height )]] | ||
+ | |||
+ | |||
+ | *[[void operator delete ( void *obj )]] | ||
+ | |||
+ | |||
+ | |||
+ | *[[BinaryDataAutoPtr ()]] | ||
+ | *[[BinaryDataAutoPtr ( const BinaryData &sourceData )]] | ||
+ | |||
+ | |||
+ | |||
+ | =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 |
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
- BinaryData &operator = ( const BinaryData &source )
- bool operator == ( const BinaryData &compareData ) const
- bool operator != ( const BinaryData &compareData ) const
- int GetCount () const
- int GetIndex ( const QuadChar& dataType ) const
- ulong GetTotalSize () const
- void GetType ( int index, QuadChar& dataType ) const
- ulong GetSize ( int index ) const
- errcode GetData ( int index, ulong offset, ulong amount, void *buffer ) const
- errcode Add ( const QuadChar& dataType, ulong amount, void *buffer )
- bool Remove ( const QuadChar& dataType )
- void RemoveAll ()
- errcode GetFNAMData ( Text &filepathlist ) const
- errcode AddFNAMData ( const Text &filepathlist )
- errcode GetSIZEData ( short &width, short &height ) const
- errcode AddSIZEData ( short width, short height )
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