XStream® HDVR® QuickStart Basic Application

<< Click to Display Table of Contents >>

Navigation:  XStream® HDVR® QuickStart Guides >

XStream® HDVR® QuickStart Basic Application

Previous pageReturn to chapter overviewNext page

C++ C++ Java Java .NET .NET

Summary

 

The Client_QuickStartBasic_Cpp example application is the most basic of the C++ example applications. It serves as the starting point for learning how to write a XStream® HDVR® application in C++. The Client_QuickStartBasic_Cpp example application is a platform independent console application with no graphical user interface. The application outlines the minimal code necessary to initialize the XStream HDVR SDK, load a dataset, render an image and save it to disk.

 

This page summarizes the Client_QuickStartBasic_Cpp example application. For a more detailed overview of basic application concepts, see the Basic Functionality chapter.

 

Initialization

 

The following code initializes the XStream HDVR SDK by creating an ILibrary object. The ILibrary object is used to allocate memory for certain XStream HDVR class objects. The ILibrary object is then used to allocate an instance of the IServerContext class. The IServerContext class is used to connect to the XStream HDVR server, load datasets, and to allocate memory for XStream HDVR class objects that exist in both the client and server applications.

 

ILibrary *pLibrary;

IServerContext *pServer;

 

// This function opens a view of the library. This library interface is your main

// entry point into the rest of the API.

RRESULT rr = hdrcclientOpenLibrary(HDRCCLIENT_SDK_VERSION, &pLibrary);

 

// The following macro tests to see if rr is an error code. If so, it returns that

// error code. Otherwise, it continues. This can be a useful funciton to use all

// throughout your code. For the sake of readability, this will be the last time it is

// shown here

RETONFAILED(rr);

 

// We use the library to create a server connection context.

rr = pLibrary->CreateObject(&CLSID_ServerContext, &pServer);

 

Connecting to the Server

 

Once the ILibrary object has been initialized and an IServerContext object created, the application can connect to the server with the IServerContext::Connect() method. The IServerContext::Connect() method will return an integer value designating either success, or a failure code indicating the type of problem. The IServerContext::Connect() return codes are enumerated in the code below.

 

// This connects to a server on the local machine using the default port (6778). Replace

// localhost with the IP address of a remote machine to connect to that. 

h_int32 serverResult;

rr = pServer->Connect("localhost", 6778, NULL, &serverResult);

if (FAILED(rr)){

   cout << "Unable to connect with server." << endl;

   return -1;

}

switch(serverResult) {

   case 0:

      cout << "server has too many connections";

      return -1;

   case -2:

      cout << "server cannot be found, invalid address";

      return -1;

   case -3:

      cout << "server has an invalid license";

      return -1;

   default:

      cout << "server connection established";

}

 

cout << "Connected to server." << endl;

 

Loading a Dataset

 

Once a connection has been made to the server, a dataset can be loaded. The simplest way to load a dataset is with the IServerContext::LoadDicomDirectory() method. Other dataset loading methods are also available in the IServerContext class. After a dataset has been loaded, an IOctreeContext data structure can be created. The IOctreeContext class is necessary to render datasets with the various adaptive render engines. For more information on engine modes see the Rendering Modes page.

 

// Load a sample DICOM dataset.

// Note that this path is relative to the SERVER application working directory.

IVolumeDataContext *pData;

cout << "Loading DICOM dataset..." << endl;

rr = pServer->LoadDicomDirectory(&pData, "../../../Datasets/Head_Neck/");

cout << "DICOM dataset finished loading." << endl;

 

// Exit if DICOM loading fails.

if(FAILED(rr)) {

   cout << "Failed to load DICOM dataset." << endl;

   return -1;

}

 

// Unless you are only rendering in brute force RT_THIN* rendering modes, you will need an octree. 

// This data structure greatly accelerates rendering on adaptive modes.

IOctreeContext *pOctree;

rr = pServer->CreateOctree(&pOctree, pData);

 

Creating the RenderEngineContext

 

A render engine is the object responsible for rendering images that are displayed to the user. There are two render engine class types, IRenderEngineContext and IRenderQueue. The IRenderEngineContext class renders images with synchronous, blocking method calls. The IRenderQueue class renders images with asynchronous, non-blocking method calls. The Client_QuickStartBasic_Cpp application uses the IRenderEngineContext class because its synchronous nature makes it simpler to use with the Client_QuickStartBasic_Cpp application. Most applications will use the IRenderQueue class because it automates aspects of managing the render engine state and image generation. For more information on the difference between the IRenderEngineContext and IRenderQueue classes, see the RenderEngineContext vs. RenderQueue section. The following code illustrates allocation and initialization of an IRenderEngineContext object.

 

// Create a render engine so we can create images.

IRenderEngineContext *pEngine;

// Note that the RECID_* parameters are legacy and are ignored internally.

rr = pServer->CreateRenderEngine(&pEngine,  RECID_PAR);

 

// Apply the data and octree to the engine for rendering.

rr = pEngine->SetVolumeData(pData, pOctree);

 

Setting Render Parameters

 

The final appearance of images generated by the XStream HDVR render engine is determined by values set in a RENDER_PARAMS structure. Details on the various fields available in the RENDER_PARAMS structure are discussed in the Render Parameters section. Values in the RENDER_PARAMS structure can either be set programmatically, or by loading a set of render parameters from an XML file. The application applies render parameters by loading one of the saved XML preset files provided with the XStream HDVR SDK. For more information on saving and loading XML preset files, see the Managing Presets section. Once a RENDER_PARAMS structure has been set, it is applied with the IRenderEngineContext::SetRenderParams() method. The following code illustrates loading an XML preset file and applying to the render engine.

 

// What follows is an example of a manually set series of rendering parameters. Note that Mask dictates which parameters

// are valid and will be set. Similarly, FlagsMask will tell you which of the bits in Flags are valid and will be set.

// See the function level documentation for what each of the parameters do.

RENDER_PARAMS rp;

BCOM_ZEROMEMORY(rp);

rp.cbSize = sizeof(rp);

 

// Create an IPresetUtils object to load a saved preset XML file.

IPresetUtils *pPresetUtils;

rr = pLibrary->CreateObject(&CLSID_PresetUtils, &pPresetUtils);

 

cout << "Loading XML preset file." << endl;

 

// Load a saved XML preset into a RENDER_PARAMS structure.

// Note that this path is relative to the CLIENT directory.

// Presets can be loaded relative to the SERVER using the IServerContext::LoadPreset() method.

rr = pPresetUtils->ReadParamsFromXml("../../../Preset Library/3D_bone.xml", &rp);

 

// Exit if preset loading fails.

if(FAILED(rr)) {

 cout << "Failed to load preset file." << endl;

 return -1;

}

 

// Apply the loaded RENDER_PARAMS to the engine.

pEngine->SetRenderParams(&rp);

 

Rendering an Image

 

Once a set of RENDER_PARAMS has been applied to a render engine, an image can be generated. Two types of images that can be generated: interactive quality, and final quality. Interactive quality images can be rendered faster than final quality images, but have somewhat lower resolution, depending on the speed of the computer and quality settings applied in the RENDER_PARAMS structure. Final quality images have higher detail levels than interactive quality images, but take a little longer to render. Note that before a final quality image can be rendered, an identical interactive quality image must be rendered immediately prior to the final quality image. Rendering an image is demonstrated in the code below.

 

Note that before a final quality image can be rendered, an identical interactive quality image must be rendered immediately prior to the final quality image.

 

// These structures will be used for rendering. imReq describes the image as we request it to be

// rendered  (interactive or final or other details). imRes describes the image that comes back

// from rendering. Note that imReq does not need the image size. That is specified by the RENDER_PARAMS

// you have set.

VOLVISIMAGE imReq,imRes;

BCOM_ZEROMEMORY(imReq);

BCOM_ZEROMEMORY(imRes);

 

// We get the default image allocator so we can free image buffers as they come in. Alternatively,

// you may specify how images are allocated by calling pEngine->SetImageAllocator(yourAllocator).

IAllocator *alloc;

pLibrary->GetAllocator(HCAP_IMAGE, &alloc);

 

cout << "Rendering image." << endl;

 

// Render an interactive image (lower quality, but may be displayed rapidly for such things as mouse interaction).

// It is required to render an interactive image before a final quality image can be rendered.

imReq.Stage = RENDER_STAGE_PROGR0;

rr = pEngine->Render(&imReq, &imRes);

 

// When recieving an image, the client will use the following logic to ensure that buffers are not created and destroyed unnecessarily:

//   -If imRes.Data is NULL, the appropriately sized buffer will be created and filled with the image data

//   -If imRes.Data is not NULL, the size of that buffer (as specified by imRes.Datasize) will be compared with the

//    size needed by the image coming down the stream. If the size is the same, the image will be streamed directly

//    into this buffer. If the size is different, the buffer will be reallocated to the new size.

//

// Note that this logic implies that the user should check to see if the Datasize has changed. If so, then they should free

// the old image buffer. If it has not, there is no need to do anything. Since we are at the first render in this application, we

// don't need to do this test until after the second render. Let's hold onto a copy of the image properties to do this:

VOLVISIMAGE oldImage = imRes;

 

// Render a final image (higher quality, but not intended for interaction). IMPORTANT: You MUST create an interactive

// image with identical rendering parameters before creating a final render. The final render uses information created

// by the interactive stage.

imReq.Stage = RENDER_STAGE_FINAL;

rr = pEngine->Render(&imReq, &imRes);

if(oldImage.Data != imRes.Data) {

   // The image was reallocated. We must free the old image. Note that this will never be true in this example because

   // we don't change the image size between rendering frames.

   alloc->Free(oldImage.Data);

}

 

Saving the Image to Disk

 

Since the Client_QuickStartBasic_Cpp application has no graphical user interface, the application saves the rendered image to disk as a JPEG file so the developer can view it. The ability to save JPEG images is provided by an open source utility included with the sample application; it is not built into the XStream HDVR SDK or the C++ language.

 

// Save rendered image to JPEG file.

cout << "Saving image as JPEG file." << endl;

jpge::compress_image_to_jpeg_file( "BasicCppAppImage.jpg", imRes.Width, imRes.Height, 3, (jpge::uint8 *)imRes.Data );

 

Releasing System Resources

 

In the above sample code, various system resources were allocated, including the IServerContext, IVolumeDataContext, IOctreeContext, and IRenderEngineContext objects. When these resources are no longer needed, the resources must be released and deallocated in the proper fashion. Typically, the IServerContext is allocated when the application is started and released upon termination, but when data is no longer visualized within the application it should be released. While the client-side may consume a limited amount of resources, a large volume may exist on the server. It is the responsibility of the client to ensure that all of its server-side resources are released when they are no longer needed.

 

XStream HDVR C++ based client-side context class objects are allocated with XStream HDVR SDK utility methods, and are controlled with a reference counting mechanism. Client-side methods exist that tell the server to deallocate unneeded resources. The developer should never attempt to manually delete such objects. Reference counting methods are only available in the C++ Client API. The Java/.NET languages implement reference counting mechanisms specific to Java/.NET, therefore the XStream HDVR SDK reference counting mechanism is not implemented in the Java/.NET API. For more information on XStream HDVR memory management, see Memory Management.

 

Most XStream HDVR classes in the C++ API use reference counting to control object deallocation. When an object is created, its reference count is set to 1. Whenever a reference to that object is made by assigning a pointer to it, its reference count should be incremented with the object's IncRef() method. When a reference to an object is removed, its reference count should be decremented with the object's DecRef() method. When an object's reference count reaches 0 the object is automatically deallocated. Deallocating a client-side object by calling its DecRef() method will NOT automatically cause the server-side representation of this object to be deallocated. The appropriate ReleaseSessionResources() method must be called for that object type to ensure that server-side resources are deallocated.

 

The Java and C# languages automatically implement their own reference counting and memory management mechanisms, so the IncRef() and DecRef() methods do not exist in these APIs.

 

Each server-side resource has a corresponding client-side resource that must manage and release its reference to the server-side resource.

For example, when an application loads a dataset from the client application a client-side IVolumeDataContext object is created, and a server-side IVolumeData object is created.

 

In C++, when the application no longer needs this dataset, the application should first call IVolumeDataContext::ReleaseSessionResources() to signal the server that it can delete its IVolumeData object. The application should then call the IVolumeDataContext::DecRef() method once for each terminated reference to the object so that its reference count goes down to 0. At this point the IVolumeDataContext object will be deleted. The following code shows the proper method for deallocating resources.

 

// Free up server resources for the engine if the reference count becomes 0 (in this case it will).

rr = pEngine->ReleaseSessionResources();

// Free up client resources for the engine if the reference count becomes 0 (in this case it will).

rr = pEngine->DecRef();

// Free up server resources for the octree if the reference count becomes 0 (in this case it will).

rr = pOctree->ReleaseSessionResources();

// Free up client resources for the octree if the reference count becomes 0 (in this case it will).

rr = pOctree->DecRef();

// Free up server resources for the data if the reference count becomes 0 (in this case it will).

rr = pData->ReleaseSessionResources();

// Free up client resources for the data if the reference count becomes 0 (in this case it will).

rr = pData->DecRef();

 

// Disconnect from the server

pServer->Disconnect();

 

// Release our server and library objects as well

pServer->DecRef();

pLibrary->DecRef();

 

Summary

 

The Client_QuickStartBasic_Java example application is the most basic of the Java example applications. It serves as the starting point for learning how to write a XStream® HDVR® application in Java. The Client_QuickStartBasic_Java example application is a platform independent console application with no graphical user interface. The application outlines the minimal code necessary to initialize the XStream HDVR SDK, load a dataset, render an image, and save it to disk.

 

This page summarizes the Client_QuickStartBasic_Java example application. For a more detailed overview of basic application concepts, see the Basic Functionality chapter.

 

Connecting to the Server

 

The first step in writing a XStream HDVR Java application is to connect to the rendering server which is done by creating an instance of the hdvrServerContext class. The hdvrServerContext constructor takes the network address and port number of the rendering server as input parameters. The constructor will throw an exception if any error occurs during the connection attempt.

 

hdrcServerContext server = null;

 

try {

   // connect to the server. this will throw if the connection fails.

   server = new hdrcServerContext("localhost", 6778);

} catch(hdrcServerContext.ServerBusyIOException ex ) {

   System.out.println("Server busy."); 

   return;                

} catch(IOException ex) {

   System.out.println(ex.getMessage()); 

   return;

 

System.out.println("Connected to server.");

 

Loading a Dataset

 

Once a connection has been made to the server, a dataset can be loaded. The simplest way to load a dataset is with the hdrcServerContext::loadDicomDirectory() method. Other dataset loading methods are also available in the hdrcServerContext class. After a dataset has been loaded, an hdrcOctreeContext data structure can be created. The hdrcOctreeContext class is necessary to render datasets with the various adaptive render engines. For more information on engine modes, see the Rendering Modes page.

 

hdrcVolumeDataContext volumeData = null;

 

System.out.println("Loading DICOM dataset...");

 

try {

   // Load a sample DICOM dataset.

   // Note that this path is relative to the SERVER application working directory.

   volumeData = server.loadDicomDirectory("../../../Datasets/Head_Neck/");

} catch(IOException ex) {

   System.out.println("Could not load DICOM dataset.");

   return;

}

if(volumeData == null) {

   System.out.println("Could not load DICOM dataset.");

   return;

}

 

System.out.println("DICOM dataset finished loading.");

 

// Unless you are only rendering in brute force RT_THIN* rendering modes, you will need an octree. 

// This data structure greatly accelerates rendering on adaptive modes.

hdrcOctreeContext octreeData = null;

try {

   octreeData = server.createOctree(volumeData.getId());

} catch(IOException ex) {

   System.out.println("Failed to create octree.");

   return;

}

 

Creating the RenderEngineContext

 

A render engine is the object responsible for rendering images that are displayed to the user. There are two render engine class types, hdrcRenderEngineContext and hdrcRenderQueue. The hdrcRenderEngineContext class renders images with synchronous, blocking method calls. The hdrcRenderQueue class renders images with asynchronous, non-blocking method calls. The Client_QuickStartBasic_Java application uses the hdrcRenderEngineContext class due to its synchronous nature. Most other applications will use the hdrcRenderQueue class because it automates aspects of managing the render engine state and image generation. For more information on the difference between the hdrcRenderEngineContext and hdrcRenderQueue classes, see the RenderEngineContext vs. RenderQueue section. The following code illustrates allocation and initialization of an hdrcRenderEngineContext object.

 

// Create a render engine so we can create images.

// Note that the RENDER_ENGINE_ID_* parameters are legacy and are ignored internally.

hdrcRenderEngineContext renderEngine = null;

try {

   renderEngine = server.createRenderEngine(hdrcDefines.RENDER_ENGINE_ID_PAR);

} catch(IOException ex) {

   System.out.println("Failed to create render engine.");

   return;

 }

        

// Apply the data and octree to the engine for rendering.

try {

   renderEngine.setVolumeData(volumeData, octreeData);

} catch(IOException ex) {

   System.out.println("Could not apply volume data to engine.");

   return;

}

 

Setting Render Parameters

 

The final appearance of images generated by the XStream HDVR render engine is determined by values set in a RENDER_PARAMS structure. The Render Parameters section discusses in more detail the various fields available in the RENDER_PARAMS structure. Values in the RENDER_PARAMS structure can either be set programmatically, or by loading a set of render parameters from an XML file. The application applies render parameters by loading one of the saved XML preset files provided with the XStream HDVR SDK. For more information on saving and loading XML preset files, see the Managing Presets section. Once a RENDER_PARAMS structure has been set, it is applied with the IRenderEngineContext::SetRenderParams() method. The following code illustrates loading an XML preset file and applying to the render engine.

 

RENDER_PARAMS rp = new RENDER_PARAMS();

 

System.out.println("Loading XML preset file.");

 

// Load a saved XML preset into a RENDER_PARAMS structure.

// Note that this path is relative to the CLIENT directory.

// Presets can be loaded relative to the SERVER using the hdrcServerContext::loadPreset() method.

boolean rr = rp.loadFromXML("../../../Preset Library/3D_bone.xml");

 

if( rr == false) {

   System.out.println("Failed to load preset file.");

   return;

}

 

// Apply the loaded RENDER_PARAMS to the engine.

try {

   renderEngine.setRenderParams(rp);

} catch (IOException e) {

   System.out.println("Could not set RENDER_PARAMS.");

   e.printStackTrace();

}

 

Rendering an Image

 

Once a set of RENDER_PARAMS has been applied to a render engine, an image can be generated. There are two types of images that can be generated: interactive quality, and final quality. Interactive quality images can be rendered faster than final quality images, but have somewhat lower resolution, depending on the speed of the computer and quality settings applied in the RENDER_PARAMS structure. Final quality images have higher detail levels than interactive quality images, but take a little longer to render. Note that before a final quality image can be rendered, an identical interactive quality image must be rendered immediately previous to the final quality image. Rendering an image is demonstrated in the code below.

 

Note that before a final quality image can be rendered, an identical interactive quality image must be rendered immediately previous to the final quality image.

 

// These structures will be used for rendering. imReq describes the image as we request it to be

// rendered  (interactive or final or other details). imRes describes the image that comes back

// from rendering. Note that imReq does not need the image size. That is specified by the RENDER_PARAMS

// you have set.

VOLVISIMAGE imReq = new VOLVISIMAGE();

VOLVISIMAGE imRes = new VOLVISIMAGE();

 

System.out.println("Rendering image.");

 

// Render an interactive image (lower quality, but may be displayed rapidly for such things as mouse interaction).

// It is required to render an interactive image before a final quality image can be rendered.

imReq.Stage = hdrcDefines.RENDER_STAGE_PROGR0;

try {

   renderEngine.render(imReq, imRes);

} catch (IOException e) {

   System.out.println("Could not render image.");

   e.printStackTrace();

}

 

// Render a final image (higher quality, but not intended for interaction). IMPORTANT: You MUST create an interactive

// image with identical rendering parameters before creating a final render. The final render uses information created

// by the interactive stage.

imReq.Stage = hdrcDefines.RENDER_STAGE_FINAL;

try {

   renderEngine.render(imReq, imRes);

} catch (IOException e) {

   System.out.println("Could not render image.");

   e.printStackTrace();

}

 

Saving the Image to Disk

 

Since the Client_QuickStartBasic_Java application has no graphical user interface, the application saves the rendered image to disk as a JPEG file so the developer can view it. The ability to save JPEG images is supported by a utility function that makes use of the JDK to save in JPEG format.

 

// Save rendered image to JPEG file.

System.out.println("Saving image as JPEG file.");

SaveToJpeg("BasicJavaAppImage.jpg", imRes.Data, imRes.Width, imRes.Height);

 

Releasing System Resources

 

The Java language implements an automatic reference counting and memory management system. Therefore, there is no need to manage memory for client side resources. However, the XStream HDVR SDK does provide a mechanism for deallocation of server side resources if they are no longer needed. The server will automatically deallocate all resources associated with a client session when that client disconnects from the server. If the application needs to manually deallocate server side resources, this can be done with a call to the releaseSessionResources() method. The hdrcVolumeDataContext, hdrcOctreeContext, and hdrcRenderEngineContext classes each provide a releaseSessionResources() method that can be called if necessary.

 

Summary

 

The Client_QuickStartBasic_Net example application is the most basic of the .NET example applications. It serves as the starting point for learning how to write a XStream® HDVR® SDK application in .NET. The Client_QuickStartBasic_Net example application is a console application with no graphical user interface. The application outlines the minimal code necessary to initialize the XStream HDVR SDK, load a dataset, render an image and save it to disk.

 

This page summarizes the Client_QuickStartBasic_Net example application. For a more detailed overview of basic application concepts, see the Basic Functionality chapter.

 

Connecting to the Server

 

The first step in writing a XStream HDVR .NET application is to connect to the rendering server which is done by creating an instance of the hdvrServerContext class. The hdvrServerContext constructor takes the network address and port number of the rendering server as input parameters. The constructor will throw an exception if any error occurs during the connection attempt.

 

hdrcServerContext server = null;

 

try {

   // connect to the server. this will throw if the connection fails.

   server = new hdrcServerContext("localhost", 6778);

}

catch(java.lang.Exception ex) {

   if (ex is java.io.IOException || ex is UnknownHostException || ex is hdrcServerContext.ServerBusyIOException)

   {

      System.Console.Write(ex.getMessage()); 

   }                

   return;

}

 

System.Console.Write("Connected to server.\n");

 

Loading a Dataset

 

Once a connection has been made to the server, a dataset can be loaded. The simplest way to load a dataset is with the hdrcServerContext::loadDicomDirectory() method. Other dataset loading methods are also available in the hdrcServerContext class. After a dataset has been loaded, an hdrcOctreeContext data structure can be created. The hdrcOctreeContext class is necessary to render datasets with the various adaptive render engines. For more information on engine modes see the Rendering Modes page.

 

hdrcVolumeDataContext volumeData = null;

 

System.Console.Write("Loading DICOM dataset...\n");

 

try {

   // Load a sample DICOM dataset.

   // Note that this path is relative to the SERVER application working directory.

   volumeData = server.loadDicomDirectory("../../../Datasets/Head_Neck/");

}

catch(java.io.IOException) {

   System.Console.Write("Could not load DICOM dataset.\n");

   return;

}

if(volumeData == null) {

   System.Console.Write("Could not load DICOM dataset.\n");

   return;

}

 

System.Console.Write("DICOM dataset finished loading.\n");

 

// Unless you are only rendering in brute force RT_THIN* rendering modes, you will need an octree. 

// This data structure greatly accelerates rendering on adaptive modes.

hdrcOctreeContext octreeData = server.createOctree(volumeData.getId());

 

Creating the RenderEngineContext

 

A render engine is the object responsible for rendering images that are displayed to the user. There are two render engine class types, hdrcRenderEngineContext and hdrcRenderQueue. The hdrcRenderEngineContext class renders images with synchronous, blocking method calls. The hdrcRenderQueue class renders images with asynchronous, non-blocking method calls. The Client_QuickStartBasic_Net application uses the hdrcRenderEngineContext class because its synchronous nature makes it simpler to use with the Client_QuickStartBasic_Net application. Most applications will use the hdrcRenderQueue class because it automates aspects of managing the render engine state and image generation. For more information on the difference between the hdrcRenderEngineContext and hdrcRenderQueue classes, see the RenderEngineContext vs. RenderQueue section. The following code illustrates allocation and initialization of an hdrcRenderEngineContext object.

 

// Create a render engine so we can create images.

// Note that the RENDER_ENGINE_ID_* parameters are legacy and are ignored internally.

hdrcRenderEngineContext renderEngine = server.createRenderEngine(hdrcDefines.__Fields.RENDER_ENGINE_ID_PAR);

 

// Apply the data and octree to the engine for rendering.

renderEngine.setVolumeData(volumeData, octreeData);

 

Setting Render Parameters

 

The final appearance of images generated by the XStream HDVR render engine is determined by values set in a RENDER_PARAMS structure. The Render Parameters section discusses in more detail the various fields available in the RENDER_PARAMS structure. Values in the RENDER_PARAMS structure can either be set programmatically, or by loading a set of render parameters from an XML file. The application applies render parameters by loading one of the saved XML preset files provided with the XStream HDVR SDK. For more information on saving and loading XML preset files, see the Managing Presets section. Once a RENDER_PARAMS structure has been set, it is applied with the IRenderEngineContext::SetRenderParams() method. The following code illustrates loading an XML preset file and applying to the render engine.

 

RENDER_PARAMS rp = new RENDER_PARAMS();

 

System.Console.Write("Loading XML preset file.\n");

 

// Load a saved XML preset into a RENDER_PARAMS structure.

// Note that this path is relative to the CLIENT directory.

// Presets can be loaded relative to the SERVER using the hdrcServerContext::loadPreset() method.

bool rr = rp.loadFromXML("../../../Preset Library/3D_bone.xml");

 

if( rr == false) {

   System.Console.Write("Failed to load preset file.\n");

   return;

}

 

// Apply the loaded RENDER_PARAMS to the engine.

renderEngine.setRenderParams(rp);

 

Rendering an Image

 

Once a set of RENDER_PARAMS has been applied to a render engine, an image can be generated. Two types of images can be generated: interactive quality and final quality. Interactive quality images can be rendered faster than final quality images, but have somewhat lower resolution, depending on the speed of the computer and quality settings applied in the RENDER_PARAMS structure. Final quality images have higher detail levels than interactive quality images, but take a little longer to render. Note that before a final quality image can be rendered, an identical interactive quality image must be rendered immediately prior to the final quality image. Rendering an image is demonstrated in the code below.

 

Note that before a final quality image can be rendered, an identical interactive quality image must be rendered immediately prior to the final quality image.

 

// These structures will be used for rendering. imReq describes the image as we request it to be

// rendered  (interactive or final or other details). imRes describes the image that comes back

// from rendering. Note that imReq does not need the image size. That is specified by the RENDER_PARAMS

// you have set.

VOLVISIMAGE imReq = new VOLVISIMAGE();

VOLVISIMAGE imRes = new VOLVISIMAGE();

 

System.Console.Write("Rendering image.\n");

 

// Render an interactive image (lower quality, but may be displayed rapidly for such things as mouse interaction).

// It is required to render an interactive image before a final quality image can be rendered.

imReq.Stage = hdrcDefines.__Fields.RENDER_STAGE_PROGR0;

renderEngine.render(imReq, imRes);

 

// Render a final image (higher quality, but not intended for interaction). IMPORTANT: You MUST create an interactive

// image with identical rendering parameters before creating a final render. The final render uses information created

// by the interactive stage.

imReq.Stage = hdrcDefines.__Fields.RENDER_STAGE_FINAL;

renderEngine.render(imReq, imRes);

 

Saving the Image to Disk

 

Since the Client_QuickStartBasic_Net application has no graphical user interface, the application saves the rendered image to disk as a JPEG file so the developer can view it. The ability to save JPEG images is supported by a utility function that makes use of the .NET framework to save in JPEG format.

 

// Save rendered image to JPEG file.

System.Console.Write("Saving image as JPEG file.\n");

SaveToJpeg("BasicNetAppImage.jpg", imRes.Data, imRes.Width, imRes.Height);

 

Releasing System Resources

 

The C# language implements an automatic reference counting and memory management system. Therefore, there is no need to manage memory for client side resources. However, the XStream HDVR SDK does provide a mechanism for deallocation of server side resources if they are no longer needed. The server will automatically deallocate all resources associated with a client session when that client disconnects from the server. If the application needs to manually deallocate server side resources, this can be done with a call to the releaseSessionResources() method. The hdrcVolumeDataContext, hdrcOctreeContext, and hdrcRenderEngineContext classes each provide a releaseSessionResources() method that can be called if necessary.