Files
yanshui177 6dcd378738 完善目录结构
完善了目录结构,添加了以前的web段com组件调用的代码(在/测试目录下)(部署没有使用到)
2017-05-17 20:43:16 +08:00

864 lines
25 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "_highgui.h"
#include <Carbon/Carbon.h>
#include <unistd.h>
#include <cstdio>
#include <cmath>
//#define MS_TO_TICKS(a) a*3/50
#define LABELWIDTH 64
#define INTERWIDGETSPACE 16
#define WIDGETHEIGHT 32
#define NO_KEY -1
struct CvWindow;
typedef struct CvTrackbar
{
int signature;
ControlRef trackbar;
ControlRef label;
char* name;
CvTrackbar* next;
CvWindow* parent;
int* data;
int pos;
int maxval;
CvTrackbarCallback notify;
}
CvTrackbar;
typedef struct CvWindow
{
int signature;
char* name;
CvWindow* prev;
CvWindow* next;
WindowRef window;
CGImageRef imageRef;
int imageWidth;//FD
int imageHeight;//FD
CvMat* image;
CvMat* dst_image;
int converted;
int last_key;
int flags;
CvMouseCallback on_mouse;
void* on_mouse_param;
struct {
int pos;
int rows;
CvTrackbar* first;
}
toolbar;
int trackbarheight;
}
CvWindow;
static CvWindow* hg_windows = 0;
#define Assert(exp) \
if( !(exp) ) \
{ \
printf("Assertion: %s %s: %d\n", #exp, __FILE__, __LINE__);\
assert(exp); \
}
static int wasInitialized = 0;
static char lastKey = NO_KEY;
OSStatus keyHandler(EventHandlerCallRef hcr, EventRef theEvent, void* inUserData);
static pascal OSStatus windowEventHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *inUserData);
static const EventTypeSpec applicationKeyboardEvents[] =
{
{ kEventClassKeyboard, kEventRawKeyDown },
};
CV_IMPL int cvInitSystem( int argc, char** argv )
{
OSErr err = noErr;
if( !wasInitialized )
{
hg_windows = 0;
err = InstallApplicationEventHandler(NewEventHandlerUPP( keyHandler),GetEventTypeCount(applicationKeyboardEvents),applicationKeyboardEvents,NULL,NULL);
if (err != noErr)
{
fprintf(stderr,"InstallApplicationEventHandler was not ok\n");
}
wasInitialized = 1;
}
return 0;
}
// TODO: implement missing functionality
CV_IMPL int cvStartWindowThread()
{
return 0;
}
static int icvCountTrackbarInWindow( const CvWindow* window)
{
CvTrackbar* trackbar = window->toolbar.first;
int count = 0;
while (trackbar != 0) {
count++;
trackbar = trackbar->next;
}
return count;
}
static CvTrackbar* icvTrackbarByHandle( void * handle )
{
CvWindow* window = hg_windows;
CvTrackbar* trackbar = NULL;
while( window != 0 && window->window != handle) {
trackbar = window->toolbar.first;
while (trackbar != 0 && trackbar->trackbar != handle)
trackbar = trackbar->next;
if (trackbar != 0 && trackbar->trackbar == handle)
break;
window = window->next;
}
return trackbar;
}
static CvWindow* icvWindowByHandle( void * handle )
{
CvWindow* window = hg_windows;
while( window != 0 && window->window != handle)
window = window->next;
return window;
}
CV_IMPL CvWindow * icvFindWindowByName( const char* name)
{
CvWindow* window = hg_windows;
while( window != 0 && strcmp(name, window->name) != 0 )
window = window->next;
return window;
}
static CvTrackbar* icvFindTrackbarByName( const CvWindow* window, const char* name )
{
CvTrackbar* trackbar = window->toolbar.first;
while (trackbar != 0 && strcmp( trackbar->name, name ) != 0)
trackbar = trackbar->next;
return trackbar;
}
//FD
/* draw image to frame */
static void icvDrawImage( CvWindow* window )
{
Assert( window != 0 );
if( window->imageRef == 0 ) return;
CGContextRef myContext;
CvTrackbar* t;
CGRect rect;
Rect portrect;
int width = window->imageWidth;
int height = window->imageHeight;
GetWindowPortBounds(window->window, &portrect);
if( window->flags & CV_WINDOW_AUTOSIZE )
{
CGPoint origin = {0,0};
CGSize size = {portrect.right-portrect.left, portrect.bottom-portrect.top-window->trackbarheight};
rect.origin = origin; rect.size = size;
}
else
{
CGPoint origin = {0, portrect.bottom - height - window->trackbarheight};
CGSize size = {width, height};
rect.origin = origin; rect.size = size;
}
/* To be sybnchronous we are using this, better would be to susbcribe to the draw event and process whenever requested, we might save SOME CPU cycles*/
SetPortWindowPort (window->window);
QDBeginCGContext (GetWindowPort (window->window), &myContext);
CGContextSetInterpolationQuality (myContext, kCGInterpolationLow);
CGContextDrawImage(myContext,rect,window->imageRef);
CGContextFlush(myContext);// 4
QDEndCGContext (GetWindowPort(window->window), &myContext);// 5
}
//FD
/* update imageRef */
static void icvPutImage( CvWindow* window )
{
Assert( window != 0 );
if( window->image == 0 ) return;
CGColorSpaceRef colorspace = NULL;
CGDataProviderRef provider = NULL;
int width = window->imageWidth = window->image->cols;
int height = window->imageHeight = window->image->rows;
colorspace = CGColorSpaceCreateDeviceRGB();
int size = 8;
int nbChannels = 3;
provider = CGDataProviderCreateWithData(NULL, window->image->data.ptr, width * height , NULL );
if (window->imageRef != NULL){
CGImageRelease(window->imageRef);
window->image == NULL;
}
window->imageRef = CGImageCreate( width, height, size , size*nbChannels , window->image->step, colorspace, kCGImageAlphaNone , provider, NULL, true, kCGRenderingIntentDefault );
icvDrawImage( window );
}
static void icvUpdateWindowSize( const CvWindow* window )
{
int width = 0, height = 240; /* init ˆ al taille de base de l'image*/
CvTrackbar* t;
Rect globalBounds;
GetWindowBounds(window->window, kWindowContentRgn, &globalBounds);
int minWidth = 320;
if( window->image ) {
width = MAX(MAX(window->image->width, width), minWidth);
height = window->image->height;
} else
width = minWidth;
height += window->trackbarheight;
//height +=WIDGETHEIGHT; /* 32 pixels are spearating tracbars from the video display */
globalBounds.right = globalBounds.left + width;
globalBounds.bottom = globalBounds.top + height;
SetWindowBounds(window->window, kWindowContentRgn, &globalBounds);
}
static void icvDeleteWindow( CvWindow* window )
{
CvTrackbar* trackbar;
if( window->prev )
window->prev->next = window->next;
else
hg_windows = window->next;
if( window->next )
window->next->prev = window->prev;
window->prev = window->next = 0;
cvReleaseMat( &window->image );
cvReleaseMat( &window->dst_image );
for( trackbar = window->toolbar.first; trackbar != 0; )
{
CvTrackbar* next = trackbar->next;
cvFree( (void**)&trackbar );
trackbar = next;
}
if (window->imageRef != NULL)
CGImageRelease(window->imageRef);
cvFree( (void**)&window );
}
CV_IMPL void cvDestroyWindow( const char* name)
{
CV_FUNCNAME( "cvDestroyWindow" );
__BEGIN__;
CvWindow* window;
if(!name)
CV_ERROR( CV_StsNullPtr, "NULL name string" );
window = icvFindWindowByName( name );
if( !window )
EXIT;
icvDeleteWindow( window );
__END__;
}
CV_IMPL void cvDestroyAllWindows( void )
{
while( hg_windows )
{
CvWindow* window = hg_windows;
icvDeleteWindow( window );
}
}
CV_IMPL void cvShowImage( const char* name, const CvArr* arr)
{
CV_FUNCNAME( "cvShowImage" );
__BEGIN__;
CvWindow* window;
int origin = 0;
int resize = 0;
CvMat stub, *image;
if( !name )
CV_ERROR( CV_StsNullPtr, "NULL name" );
window = icvFindWindowByName(name);
if( !window || !arr )
EXIT; // keep silence here.
if( CV_IS_IMAGE_HDR( arr ))
origin = ((IplImage*)arr)->origin;
CV_CALL( image = cvGetMat( arr, &stub ));
/*
if( !window->image )
cvResizeWindow( name, image->cols, image->rows );
*/
if( window->image &&
!CV_ARE_SIZES_EQ(window->image, image) ) {
if ( ! (window->flags & CV_WINDOW_AUTOSIZE) )//FD
resize = 1;
cvReleaseMat( &window->image );
}
if( !window->image ) {
resize = 1;//FD
window->image = cvCreateMat( image->rows, image->cols, CV_8UC3 );
}
cvConvertImage( image, window->image, (origin != 0 ? CV_CVTIMG_FLIP : 0) + CV_CVTIMG_SWAP_RB );
icvPutImage( window );
if ( resize )//FD
icvUpdateWindowSize( window );
__END__;
}
CV_IMPL void cvResizeWindow( const char* name, int width, int height)
{
CV_FUNCNAME( "cvResizeWindow" );
__BEGIN__;
CvWindow* window;
//CvTrackbar* trackbar;
if( !name )
CV_ERROR( CV_StsNullPtr, "NULL name" );
window = icvFindWindowByName(name);
if(!window)
EXIT;
SizeWindow(window->window, width, height, true);
__END__;
}
CV_IMPL void cvMoveWindow( const char* name, int x, int y)
{
CV_FUNCNAME( "cvMoveWindow" );
__BEGIN__;
CvWindow* window;
//CvTrackbar* trackbar;
if( !name )
CV_ERROR( CV_StsNullPtr, "NULL name" );
window = icvFindWindowByName(name);
if(!window)
EXIT;
MoveWindow(window->window, x, y, true);
__END__;
}
void TrackbarActionProcPtr (ControlRef theControl, ControlPartCode partCode)
{
CvTrackbar * trackbar = icvTrackbarByHandle (theControl);
if (trackbar == NULL)
{
fprintf(stderr,"Erreur recuperation trackbar\n");
return;
}
else
{
if ( trackbar->data )
*trackbar->data = GetControl32BitValue (theControl);
if ( trackbar->notify )
trackbar->notify(GetControl32BitValue (theControl));
}
}
CV_IMPL int cvCreateTrackbar (const char* trackbar_name, const char* window_name,int* val, int count, CvTrackbarCallback on_notify)
{
int result = 0;
CV_FUNCNAME( "cvCreateTrackbar" );
__BEGIN__;
/*char slider_name[32];*/
CvWindow* window = 0;
CvTrackbar* trackbar = 0;
Rect stboundsRect;
ControlRef outControl;
ControlRef stoutControl;
Rect bounds;
if( !window_name || !trackbar_name )
CV_ERROR( CV_StsNullPtr, "NULL window or trackbar name" );
if( count <= 0 )
CV_ERROR( CV_StsOutOfRange, "Bad trackbar maximal value" );
window = icvFindWindowByName(window_name);
if( !window )
EXIT;
trackbar = icvFindTrackbarByName(window,trackbar_name);
if( !trackbar )
{
int len = strlen(trackbar_name);
trackbar = (CvTrackbar*)cvAlloc(sizeof(CvTrackbar) + len + 1);
memset( trackbar, 0, sizeof(*trackbar));
trackbar->signature = CV_TRACKBAR_MAGIC_VAL;
trackbar->name = (char*)(trackbar+1);
memcpy( trackbar->name, trackbar_name, len + 1 );
trackbar->parent = window;
trackbar->next = window->toolbar.first;
window->toolbar.first = trackbar;
if( val )
{
int value = *val;
if( value < 0 )
value = 0;
if( value > count )
value = count;
trackbar->pos = value;
trackbar->data = val;
}
trackbar->maxval = count;
trackbar->notify = on_notify;
int c = icvCountTrackbarInWindow(window);
GetWindowBounds(window->window,kWindowContentRgn,&bounds);
stboundsRect.top = (INTERWIDGETSPACE +WIDGETHEIGHT)* (c-1)+INTERWIDGETSPACE;
stboundsRect.left = INTERWIDGETSPACE;
stboundsRect.bottom = stboundsRect.top + WIDGETHEIGHT;
stboundsRect.right = stboundsRect.left+LABELWIDTH;
fprintf(stdout,"create trackabar bounds (%d %d %d %d)\n",stboundsRect.top,stboundsRect.left,stboundsRect.bottom,stboundsRect.right);
CreateStaticTextControl (window->window,&stboundsRect,CFStringCreateWithCString(NULL,trackbar_name,kCFStringEncodingASCII),NULL,&stoutControl);
stboundsRect.top = (INTERWIDGETSPACE +WIDGETHEIGHT)* (c-1)+INTERWIDGETSPACE;
stboundsRect.left = INTERWIDGETSPACE*2+LABELWIDTH;
stboundsRect.bottom = stboundsRect.top + WIDGETHEIGHT;
stboundsRect.right = bounds.right-INTERWIDGETSPACE;
CreateSliderControl (window->window,&stboundsRect, trackbar->pos,0,trackbar->maxval,kControlSliderLiveFeedback,0,true,NewControlActionUPP(TrackbarActionProcPtr),&outControl);
bounds.bottom += INTERWIDGETSPACE + WIDGETHEIGHT;
SetControlVisibility (outControl,true,true);
SetControlVisibility (stoutControl,true,true);
trackbar->trackbar = outControl;
trackbar->label = stoutControl;
if (c == 1)
window->trackbarheight = INTERWIDGETSPACE*2 + WIDGETHEIGHT;
else
window->trackbarheight += INTERWIDGETSPACE + WIDGETHEIGHT;
icvUpdateWindowSize( window );
}
__END__;
return result;
}
CV_IMPL void
cvSetMouseCallback( const char* name, CvMouseCallback function, void* info)
{
CvWindow* window = icvFindWindowByName( name );
if (window != NULL)
{
window->on_mouse = function;
window->on_mouse_param = info;
}
else
{
fprintf(stdout,"Une erreur lors de cvSetMouseCallback, window nom trouvee : %s",name);
}
}
CV_IMPL int cvGetTrackbarPos( const char*, const char* )
{
//CV_NO_GTK_ERROR( "cvGetTrackbarPos" );
return -1;
}
CV_IMPL void cvSetTrackbarPos( const char*, const char*, int )
{
// CV_NO_GTK_ERROR( "cvSetTrackbarPos" );
return ;
}
CV_IMPL void* cvGetWindowHandle( const char* name )
{
WindowRef result = 0;
CV_FUNCNAME( "cvGetWindowHandle" );
__BEGIN__;
CvWindow* window;
window = icvFindWindowByName( name );
if (window != NULL)
result = window->window;
else
result = NULL;
__END__;
return NULL;
}
CV_IMPL const char* cvGetWindowName( void* window_handle )
{
const char* window_name = "";
CV_FUNCNAME( "cvGetWindowName" );
__BEGIN__;
CvWindow* window;
if( window_handle == 0 )
CV_ERROR( CV_StsNullPtr, "NULL window" );
window = icvWindowByHandle(window_handle );
if( window )
window_name = window->name;
__END__;
return window_name;
}
CV_IMPL int cvNamedWindow( const char* name, int flags )
{
int result = 0;
CV_FUNCNAME( "cvNamedWindow" );
if (!wasInitialized)
cvInitSystem(0, NULL);
// to be able to display a window, we need to be a 'faceful' application
// http://lists.apple.com/archives/carbon-dev/2005/Jun/msg01414.html
static bool switched_to_faceful = false;
if (! switched_to_faceful)
{
ProcessSerialNumber psn = { 0, kCurrentProcess };
OSStatus ret = TransformProcessType (&psn, kProcessTransformToForegroundApplication );
if (ret == noErr)
{
SetFrontProcess( &psn );
switched_to_faceful = true;
}
else
{
fprintf(stderr, "Failed to tranform process type: %d\n", (int) ret);
fflush (stderr);
}
}
__BEGIN__;
WindowRef outWindow = NULL;
OSStatus err = noErr;
Rect contentBounds = {100,100,320,440};
CvWindow* window;
UInt wAttributes = 0;
int len;
const EventTypeSpec genericWindowEventHandler[] = {
{ kEventClassMouse, kEventMouseMoved},
{ kEventClassMouse, kEventMouseUp},
{ kEventClassMouse, kEventMouseDown},
{ kEventClassWindow, kEventWindowClose },
{ kEventClassWindow, kEventWindowBoundsChanged }//FD
};
if( !name )
CV_ERROR( CV_StsNullPtr, "NULL name string" );
if( icvFindWindowByName( name ) != 0 ){
result = 1;
EXIT;
}
len = strlen(name);
CV_CALL( window = (CvWindow*)cvAlloc(sizeof(CvWindow) + len + 1));
memset( window, 0, sizeof(*window));
window->name = (char*)(window + 1);
memcpy( window->name, name, len + 1 );
window->flags = flags;
window->signature = CV_WINDOW_MAGIC_VAL;
window->image = 0;
window->last_key = 0;
window->on_mouse = 0;
window->on_mouse_param = 0;
window->next = hg_windows;
window->prev = 0;
if( hg_windows )
hg_windows->prev = window;
hg_windows = window;
wAttributes = kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute | kWindowLiveResizeAttribute;
err = CreateNewWindow ( kDocumentWindowClass,wAttributes,&contentBounds,&outWindow);
if (err != noErr)
fprintf(stderr,"Erreur while creating the window\n");
SetWindowTitleWithCFString(outWindow,CFStringCreateWithCString(NULL,name,kCFStringEncodingASCII));
if (err != noErr)
fprintf(stdout,"Erreur SetWindowTitleWithCFString\n");
window->window = outWindow;
err = InstallWindowEventHandler(outWindow, NewEventHandlerUPP(windowEventHandler), GetEventTypeCount(genericWindowEventHandler), genericWindowEventHandler, outWindow, NULL);
ShowWindow( outWindow );
result = 1;
__END__;
return result;
}
static pascal OSStatus windowEventHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void *inUserData)
{
CvWindow* window = NULL;
UInt32 eventKind, eventClass;
OSErr err = noErr;
int event = 0;
UInt32 count = 0;
HIPoint point = {0,0};
EventMouseButton eventMouseButton = 0;//FD
UInt32 modifiers;//FD
WindowRef theWindow = (WindowRef)inUserData;
if (theWindow == NULL)
return eventNotHandledErr;
window = icvWindowByHandle(theWindow);
if ( window == NULL)
return eventNotHandledErr;
eventKind = GetEventKind(theEvent);
eventClass = GetEventClass(theEvent);
switch (eventClass) {
case kEventClassMouse : {
switch (eventKind){
case kEventMouseUp :
case kEventMouseDown :
case kEventMouseMoved : {
err = CallNextEventHandler(nextHandler, theEvent);
if (err != eventNotHandledErr)
return err;
err = GetEventParameter(theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(eventMouseButton), NULL, &eventMouseButton);
err = GetEventParameter(theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
err = GetEventParameter(theEvent,kEventParamClickCount,typeUInt32,NULL,sizeof(UInt32),NULL,&count);
if (err == noErr){
if (count >1) event += 6;
} else {
event = CV_EVENT_MOUSEMOVE;
}
if (eventKind == kEventMouseUp)
event +=4;
if (eventKind == kEventMouseDown)
event +=1;
err = GetEventParameter(theEvent, kEventParamWindowMouseLocation, typeHIPoint, NULL, sizeof(point), NULL, &point);
if (eventKind != kEventMouseMoved){
switch(eventMouseButton){
case kEventMouseButtonPrimary:
if (modifiers & controlKey) /* ctrl-click for right button */
event += 1;
break;
case kEventMouseButtonSecondary:
event += 1;
break;
case kEventMouseButtonTertiary:
event += 2;
break;
}
}
int c = icvCountTrackbarInWindow(window);
if (window->on_mouse != NULL){
int lx,ly;
Rect structure, content;
GetWindowBounds(theWindow, kWindowStructureRgn, &structure);
GetWindowBounds(theWindow, kWindowContentRgn, &content);
lx = point.x - content.left + structure.left;
ly = point.y - window->trackbarheight - content.top + structure.top; /* minus la taille des trackbars */
if (window->flags & CV_WINDOW_AUTOSIZE) {//FD
//printf("was %d,%d\n", lx, ly);
/* scale the mouse coordinates */
lx = lx * window->imageWidth / (content.right - content.left);
ly = ly * window->imageHeight / (content.bottom - content.top - window->trackbarheight);
//printf("now %d,%d\n", lx, ly);
}
//fprintf(stdout,"final OpenCV event is %d\n",event);
//fprintf(stdout,"event @ %d %d which is localy %d %d offset of %d\n",point.h, point.v ,lx,ly,yOffset);
if (lx>0 && ly >0){ /* a remettre dans les coordonnŽes locale */
window->on_mouse(event,lx,ly,0,NULL);
}
}
}
default : return eventNotHandledErr;
}
}
case kEventClassWindow : {//FD
switch (eventKind){
case kEventWindowBoundsChanged :
{
/* resize the trackbars */
CvTrackbar *t;
Rect bounds;
GetWindowBounds(window->window,kWindowContentRgn,&bounds);
for ( t = window->toolbar.first; t != 0; t = t->next )
SizeControl(t->trackbar,bounds.right - bounds.left - INTERWIDGETSPACE*3 - LABELWIDTH , WIDGETHEIGHT);
}
/* redraw the image */
icvDrawImage(window);
break;
default :
return eventNotHandledErr;
}
}
default:
return eventNotHandledErr;
}
return eventNotHandledErr;
}
OSStatus keyHandler(EventHandlerCallRef hcr, EventRef theEvent, void* inUserData)
{
UInt32 eventKind;
UInt32 eventClass;
OSErr err = noErr;
eventKind = GetEventKind (theEvent);
eventClass = GetEventClass (theEvent);
err = GetEventParameter(theEvent, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(lastKey), NULL, &lastKey);
if (err != noErr)
lastKey = NO_KEY;
return noErr;
}
CV_IMPL int cvWaitKey (int maxWait)
{
EventRecord theEvent;
// wait at least for one event (to allow mouse, etc. processing), exit if maxWait milliseconds passed (nullEvent)
UInt32 start = TickCount();
do
{
// remaining time until maxWait is over
UInt32 wait = EventTimeToTicks (maxWait / 1000.0) - (TickCount() - start);
if (wait < 0)
wait = 0;
WaitNextEvent (everyEvent, &theEvent, maxWait > 0 ? wait : kDurationForever, NULL);
}
while (lastKey == NO_KEY && theEvent.what != nullEvent);
int key = lastKey;
lastKey = NO_KEY;
return key;
}
/* End of file. */