Frame Buffers

 

Some buffers are specific to the frames, for the most used ones (Erase, Work_GLI and Work_TAG) we have specific methods to address them:

 

Public:

Buf_Erase (void *)

Shortcut to the Erase buffer.  If this variable exist, you can access the buffer directly from it.  Otherwise, you need to use "Erase_Get()" to obtain a pointer to the buffer.

Erase_Get

 

Erase_Load

 

Erase_Restore

 

Erase_Lock

Methods to Lock and Unlock the buffer while you are working with it.

 

Erase_Unlock

 

Public:

Buf_Work_GLI (void *)

Shortcut to the Erase buffer.  If this variable exist, you can access the buffer directly from it.  Otherwise, you need to use "Work_GLI_Get()" to obtain a pointer to the buffer.

Work_GLI_Get

 

Work_GLI_Load

 

Work_GLI_Clear

 

Work_GLI_Lock

Methods to Lock and Unlock the buffer while you are working with it.

 

Work_GLI_Unlock

 

Public:

Buf_Work_TAG (void *)

Shortcut to the Erase buffer.  If this variable exist, you can access the buffer directly from it.  Otherwise, you need to use "Work_TAG_Get()" to obtain a pointer to the buffer.

Work_TAG_Get

 

Work_TAG_Load

 

Work_TAG_Clear

 

Work_TAG_Lock

Methods to Lock and Unlock the buffer while you are working with it.

 

 

Work_TAG_Unlock

 

Public:

Buf_Disp_GLI (void *)

Shortcut to the buffers.  If this variable exist, you can access the buffers directly from them.  Otherwise, you need to use "User_Get()" mechanism to obtain a pointer to the buffer.

 

 

 

 

Buf_Disp_TAG (void *)

Buf_Mask (void *)

Buf_Filter (void *)

Buf_Gradient (void *)

 

 

The Erase buffer

 

The erase buffer is used for all the "Erase" operation. When the user use the right mouse button to "erase", he actually paint with the content of the erase buffer.

 

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

//

//          Function:          Ima_Click

//

//          Parameters:          up_down (int)          state of the mouse buttons

//          Returns:          (int)                    1 if action was used

//

//          This is called each time the cursor is inside an image and the state

//          of the mouse buttons has changed.

//

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

extern "C" __declspec(dllexport) int Ima_Click( HWND wnd, SliceO_Window *window, int up_down, short x, short y )

{

 

          if ( ! up_down ) {

                    // --- stop recording ---

                    Undo_Pixel_TAG *pix_list = Undo_Pixel_Stop_TAG() ;

 

                    // --- if there's a list, add it to an undo structure ---

                     if ( pix_list ) {

                              unsigned int size = Undo_TAG_Size( pix_list ) ;

                              Undo_Save_Command( NULL, pix_list, "TAG Edit: Stroke \001(%s)", Int_2_Byte(size) ) ;

                              return( 1 ) ;

                    }

                    return( 0 ) ;

          }

 

          // --- get a pointer to the "current" frame ---

          SliceO_Frame *Frame_Ovr = (SliceO_Frame *) Fct_Variable_Value( "$FRAME_OVR" ) ;

 

          // --- only edit when over an image ---

          if ( ! Frame_Ovr )

                    return( 0 ) ;

 

          // --- only edit if the image is selected ---

          if ( ! Frame_Ovr->Flag_Get_Bit( CLASS_MODE_THIS, window, CLASS_FLAG_SELECT ) )

                    return( 0 ) ;

 

          // --- if we do not have an "erase" buffer, load it now ---

          if ( ! Frame_Ovr->Erase_Get() )

                    Frame_Ovr->Erase_Load() ;

 

          // --- start recording ---

          Undo_Pixel_Start_TAG() ;

 

          // --- we have the cursor in "Wnd" space, we want it in "Frm" space ---

          Point_2D pos_wnd( COORD_WND, x, y ) ;

          Point_2D pos_inf = window->Wnd_2_Inf( pos_wnd ) ;

          Point_2D pos_frm = Frame_Ovr->Inf_2_Frm( window, pos_inf ) ;

 

          // --- we do not want pixel fractions here ---

          pos_frm[0] = (float) ((int) pos_frm[0]) ;

          pos_frm[1] = (float) ((int) pos_frm[1]) ;

 

          switch( up_down & MOUSE_KEY_MASK ) {

 

          case MOUSE_KEY_LEFT :

                    Edit_Paint_Pix_Add( Frame_Ovr, &pos_frm ) ;

                    break ;

 

          case MOUSE_KEY_RIGHT :

                    Edit_Paint_Pix_Erase( Frame_Ovr, &pos_frm ) ;

                    break ;

 

          case MOUSE_KEY_LEFT | MOUSE_KEY_MIDDLE :

                    Edit_Paint_Full_Add( Frame_Ovr, &pos_frm ) ;

                    break ;

 

          case MOUSE_KEY_RIGHT | MOUSE_KEY_MIDDLE :

                    Edit_Paint_Full_Erase( Frame_Ovr, &pos_frm ) ;

                    break ;

          }

 

          return( 1 ) ;

}

 

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

//

//          Edit_Paint_Pix_Erase()

//

//          bring back to old tag value under the brush

//

//          Param:          frame (SliceO_Frame *)          ptr to the frame under the cursor

//                    pos_frm (Point_2D)          cursor position in FRM space

//

//          return: (void)

//

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

void  Edit_Paint_Pix_Erase( SliceO_Frame *frame, Point_2D *pos_frm )

{

short          k, l ;

 

          assert( frame ) ;

          assert( frame->Fct_Pixel_Set_TAG ) ;

 

          unsigned char **erase = frame->Erase_Get() ;

          if ( ! erase )

                    return ;

 

unsigned short *Brush_Size = (unsigned short *) Fct_Variable_Value( "$BRUSH_SIZE" ) ;

unsigned short Brush_Radius = (unsigned short) Fct_Variable_Value( "$BRUSH_RADIUS" ) ;

unsigned short Brush_Cur =   (unsigned short) Fct_Variable_Value( "$BRUSH_CUR" ) ;

unsigned char  ***Brush_Pt = (unsigned char ***) Fct_Variable_Value( "$BRUSH_PT" ) ;

 

          // --- Get the brush's parameters ---

          short b_size = Brush_Size[Brush_Cur] - 1 ;

          short b_start = Brush_Radius - b_size ;

 

          // --- compute brush position ---

          Region_2D   region( COORD_FRM,

                                  (*pos_frm)[0] - b_size,

                                  (*pos_frm)[1] - b_size,

                                  (*pos_frm)[0] + b_size,

                                  (*pos_frm)[1] + b_size ) ;

 

          // --- stay inside the image ---

          Region_2D   cull( COORD_FRM, 0, 0, frame->m_x-1, frame->m_y-1 ) ;

 

          short o_x=0 ;

          if ( region[0] < 0.0f )

                    o_x = - (int) region[0] ;

 

          short o_y=0 ;

          if ( region[1] < 0.0f )

                    o_y = - (int) region[1] ;

 

          if ( ! region.Cull( cull ) )

                    return ;

 

          int modif = 0 ;

 

          // --- change the actual tag values ---

          unsigned short TAG_Cur = (unsigned short) Fct_Variable_Value( "$TAG_CUR" ) ;

          Vect pos ;

          pos.z = frame->m_z ;

          pos.t = frame->m_t ;

          for ( pos.y = region[1], l = b_start+o_y; pos.y <= region[3]; pos.y++, l++ )

                              for ( pos.x = region[0], k = b_start+o_x; pos.x <= region[2]; pos.x++, k++ )

                                        if ( Brush_Pt[Brush_Cur][l][k] )

                                        modif |= frame->Fct_Pixel_Set_TAG( frame,

                                                  frame->m_ima,

                                                  pos,

                                                  erase[(int)pos[1]][(int)pos[0]] ) ;

 

          // --- if nothing change, get out ---

          if ( ! modif )

                    return ;

 

          // --- Update the displayed tag image ---

          Fct_Update( UPDATE_DISPLAY_TAG, CLASS_MODE_ALL, frame, &region ) ;

          Fct_Redraw( REDRAW_DB_OPENGL, CLASS_MODE_ALL, frame, &region ) ;

}

 

The Work_GLI buffer

The Work_TAG buffer

 

These buffers contain a copy of the GLI and TAG information in the same format as the original data.  The GLI data is 16 bit unsigned and the TAG is also a 16 bit unsigned. The lower byte of the Work_TAG pixels contain the 8 bit TAG value which leave another 8 bits for you (for example, when computing the "preview" in region growing, I use one of these bits). When reading pixel data, it is faster to access these buffers than to go through the database tree up to the root and access the data from the original files.  However, when changing the TAG data you have no choice but to go all the way to the files.

 

 

Example of Work_GLI and Work_TAG usage:

 

This example is extracted from the Region Growing Mode code.  The value extracted from the Work_GLI buffer is used to test if a pixel in within the threshold range of a TAG.   If it is, I mark one of the higher bits of the Work_TAG pixels under the brush.  In this mode, I also replaced the callback for the "Fct_Update_TAG_Disp" function.  In that function, I display the preview color for all pixels who have the Work_TAG bit marked (see the example for the Display_TAG_Buffer).

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

//

//          Region_Preview_Paint

//

//          Compute a preview for the "Paint" sub-mode of region growing.

//

//          input: up_down (int)          state of the mouse keys

//                 frame (SliceO_Frame *)          frame under the cursor

//                 curs_frm (Point_2D *)          position of the cursor (in FRM space)

//

//          return: (void)

//

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

void Region_Preview_Paint( int up_down, SliceO_Frame *frame, Point_2D *curs_frm )

{

          // --- first, remove the old preview ---

          Region_Preview_Erase() ;

 

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

          // --- Get the "work GLI" buffer ---

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

          int **work_gli = frame->Work_GLI_Get() ;

          if ( ! work_gli )

                    return ;

 

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

          // --- Get the "work TAG" buffer ---

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

          unsigned short **work_tag = frame->Work_TAG_Get() ;

          if ( ! work_tag )

                    return ;

 

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

          // --- Get this TAG's threshold values ---

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

          unsigned short TAG_Cur =  (unsigned short) Fct_Variable_Value( "$TAG_CUR" ) ;

          unsigned short TAG_Max =  (unsigned short)  Fct_Variable_Value( "$TAG_MAX" ) ;

          unsigned char *TAG_Lock = (unsigned char *) Fct_Variable_Value( "$TAG_LOCK" ) ;

          unsigned char          gli_flag = Region_TAG_Flag[TAG_Cur] ;

          float gli_min = Region_TAG_Min[TAG_Cur] ;

          float gli_max = Region_TAG_Max[TAG_Cur] ;

 

          unsigned short tag_mask = TAG_Max-1 ;              // = 0x00FF

 

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

          // --- now create the current preview ---

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

          Preview_Frame = frame ;

 

          // --- stay inside the image ---

          Region_2D   cull( COORD_FRM, 0, 0, frame->m_x-1, frame->m_y-1 ) ;

 

 

          unsigned short  *Brush_Size =   (unsigned short *)  Fct_Variable_Value( "$BRUSH_SIZE" ) ;

          unsigned short   Brush_Radius = (unsigned short)    Fct_Variable_Value( "$BRUSH_RADIUS" ) ;

          unsigned short   Brush_Cur =    (unsigned short)    Fct_Variable_Value( "$BRUSH_CUR" ) ;

 

          // --- Get the brush's parameters ---

          short b_size = Brush_Size[Brush_Cur] - 1 ;

          short b_start = Brush_Radius - b_size ;

 

          // --- compute brush position ---

          Preview_Region.Set( COORD_FRM,

                              curs_frm->x - (float) b_size,

                              curs_frm->y - (float) b_size,

                              curs_frm->x + (float) b_size,

                              curs_frm->y + (float) b_size ) ;

 

          // --- stay inside the image ---

          Region_2D   cull( COORD_FRM, 0, 0, frame->m_x-1, frame->m_y-1 ) ;

 

          short o_x=0 ;

          if ( Preview_Region[0] < 0.0f )

                    o_x = - (int) Preview_Region[0] ;

 

          short o_y=0 ;

          if ( Preview_Region[1] < 0.0f )

                    o_y = - (int) Preview_Region[1] ;

 

          if ( ! Preview_Region.Cull( cull ) )

                    return ;

 

          // --- change the actual tag values ---

          unsigned char ***Brush_Pt = (unsigned char ***) Fct_Variable_Value( "$BRUSH_PT" ) ;

          for ( int jj= (int) Preview_Region.min_y, l = b_start+o_y;

                              jj <= (int) Preview_Region.max_y;

                              jj++, l++ )

                    for ( int ii= (int) Preview_Region.min_x, k = b_start+o_x;

                                        ii <= (int) Preview_Region.max_x;

                                        ii++, k++ )

                              if ( Brush_Pt[Brush_Cur][l][k] ) {

                                        // --- do not touch protected tags ---

                                        if ( TAG_Lock && TAG_Lock[work_tag[jj][ii]&tag_mask] )

                                                  continue ;

                                        if ( Region_Threshold_Test( frame,

                                                                      gli_flag, gli_min, gli_max,

                                                                      work_gli[jj][ii] ) )

                                                  work_tag[jj][ii] |= 0x08000 ;

                              }

 

          // --- Update the displayed tag image ---

          Fct_Update( UPDATE_DISPLAY_TAG, CLASS_MODE_ALL, frame, &Preview_Region ) ;

          // --- we will need a redraw of this portion of the screen ---

          Fct_Redraw( REDRAW_DB_OPENGL, CLASS_MODE_ALL, frame, &Preview_Region ) ;

 

}

 

The Disp_GLI buffer

The Disp_TAG buffer

 

These buffers are used by SliceOmatic when redrawing the frames, they contain a copy of the GLI and TAG data as it is displayed.  For the GLI buffer, the data is transformed from the original 16 bit unsigned value to an 32bit RGBA value using the colormap and the brightness/contrast information.  The TAG values are transformed from the original 8 bit to 32 bit RGBA using the $TAG_RED, $TAG_GRN, $TAG_BLU and $TAG_ALPHA information.

 

Example of Display_TAG usage:

 

In the previous example, I marked one of the high bits of the Work_TAG buffer.  In this example, I assigne the "Region_Update_TAG_Disp" function to the "Fct_Update_TAG_Disp" callback. It will assign the preview color to these marked pixels.  By changing the color of the pixels in the Disp_TAG buffer, I changed the color of the pixel that will be displayed on the screen.

 

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

//

//          Function:          Region_Update_TAG_Disp

//

//          Parameters: pt (SliceO_Class *)

//          Returns:          (int)                    0 if failed, 2 if replace the default callback

//

//          This is a new Disp_TAG buffer computation function.

//

//          In this version, the highest bit of "work_tag" (bit 0x08000)

//          is set to 1 if the pixel should be of the preview color.

//

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

int Region_Update_TAG_Disp( SliceO_Class *pt )

{

          // --- first, make sure we are in the correct window ---

          SliceO_Window *Window_Cur = (SliceO_Window *) Fct_Variable_Value( "$WINDOW_CUR" ) ;

          if ( ! Window_Cur )

              return( 0 ) ;

          if ( Window_Cur->Module_Get() != Ctrl_Query() )

              return( 0 ) ;

 

          SliceO_Frame *frame = (SliceO_Frame *) pt ;

 

          Region_2D update = *(frame->Region_Update_Get()) ;

 

          // --- Get the "work TAG" buffer ---

          unsigned short **work_tag = frame->Work_TAG_Get() ;

          if ( ! work_tag )

              return( NULL ) ;

 

          // --- prepare memory for the Display TAG image ---

          if ( ! frame->Buf_Disp_TAG ) {

                    SliceO_Buffer *buf_disp_tag = pt->User_Get( ID_DISP_TAG ) ;

                    if ( ! buf_disp_tag ) {

                              // --- prepare a new buffer in the frame's user memory ---

                              buf_disp_tag = new SliceO_Buffer( PRIORITY_DISP_TAG ) ;

                              buf_disp_tag->Name_Set( "Disp TAG" ) ;

                              if ( ! buf_disp_tag->Data_New( frame->m_x * frame->m_y * sizeof(int) ) )

                                        return( 0 ) ;

                              pt->User_Set( ID_DISP_TAG, buf_disp_tag ) ;

                              // --- The image is new, we want a complete update! ---

                              update.Set( 0, 0, frame->m_x-1, frame->m_y-1 ) ;

                    }

                    frame->Buf_Disp_TAG = (void *) buf_disp_tag->Data_Get() ;

          }

 

          unsigned char *disp_tag = (unsigned char *) frame->Buf_Disp_TAG ;

          if ( ! disp_tag )

                    return( NULL ) ;

 

          unsigned short TAG_Cur =  (unsigned short) Fct_Variable_Value( "$TAG_CUR" ) ;

          unsigned short  TAG_Nb  = (unsigned short)   Fct_Variable_Value( "$TAG_NB" ) ;

          unsigned short *TAG_Red = (unsigned short *) Fct_Variable_Value( "$TAG_RED" ) ;

          unsigned short *TAG_Grn = (unsigned short *) Fct_Variable_Value( "$TAG_GRN" ) ;

          unsigned short *TAG_Blu = (unsigned short *) Fct_Variable_Value( "$TAG_BLU" ) ;

          float *TAG_Alpha = (float *) Fct_Variable_Value( "$TAG_ALPHA" ) ;

 

          frame->Work_TAG_Lock() ;

 

          // --- for pixel values, compute 0.0 - 1.0 value ---

          for ( int j= (int) update.min_y; j <= (int) update.max_y; j++ ) {

                    int k = j*frame->m_x + (int) update.min_x ;

                    for ( int i= (int) update.min_x; i <= (int) update.max_x; i++, k++ ) {

 

                              unsigned short val = work_tag[j][i] ;

 

                              // --- special case if we have a "preview" pixel ---

                              if ( Region_Preview_On && (val & 0x08000) ) {

                                        Color col ;

                                        col.color = Region_Preview_Color ;

                                        col.col.I = (int) (TAG_Alpha[TAG_Cur]*255.0f) ;

                                        ((int *) disp_tag)[k] = col.color ;

                                        continue ;

                              }

 

                              val = MIN( val, TAG_Nb ) ;

                              Color col ;

                              col.col.R = TAG_Red[val] >> 8 ;

                              col.col.G = TAG_Grn[val] >> 8 ;

                              col.col.B = TAG_Blu[val] >> 8 ;

                              col.col.I = (int) (TAG_Alpha[val]*255.0f) ;

                              ((int *) disp_tag)[k] = col.color ;

                        }

          }

 

          frame->Work_TAG_Unlock() ;

 

          // --- This callback will replace the default one ---

          return( 2 ) ;

}

 

The Mask buffer

 

Unless you plan to replace the default callbacks for "Fct_Pixel_Get_Filter", "Fct_Pixel_Get_Gradient_Amp" and "Fct_Pixel_Get_Gradient_Dir", this buffer is of no interest to you. This buffer is associated with the Filter and the Gradient buffers.  For each pixel of the frame, we have an 8 bit flag that tells us if the filtered and gradient values for this pixel have already been computed.

 

If you have "frame" a pointer to a frame, you can get a pointer to the Mask buffer with:

 

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

// --- open the Pixel Gradient buffer ---

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

if ( ! frame->Buf_Mask ) {

          SliceO_Buffer *buf_pixel_mask = frame->User_Get( ID_PIXEL_MASK ) ;

          if ( ! buf_pixel_mask ) {

                    // --- prepare a buffer in the frame's user memory ---

                    buf_pixel_mask = new SliceO_Buffer( PRIORITY_PIXEL_MASK ) ;

                    MEM_CHECK( buf_pixel_mask ) ;

                    buf_pixel_mask->Name_Set( "Pixel: Mask" ) ;

                    if ( ! buf_pixel_mask->Matrix_New( frame->m_x, frame->m_y, sizeof(unsigned char) ) )

                              return( 0 ) ;

                    frame->User_Set( ID_PIXEL_MASK, buf_pixel_mask ) ;

          }

          frame->Buf_Mask = (void *) buf_pixel_mask->Matrix_Get() ;

}

unsigned char **mask = (unsigned char **) frame->Buf_Mask ;

 

 

The Filter buffer

 

Unless you plan to replace the default callbacks for "Fct_Pixel_Get_Filter", this buffer is of no interest to you.   This buffer contain a 16 bit filtered version of the frame's GLI image

 

If you have "frame" a pointer to a frame, you can get a pointer to the Filter buffer with:

 

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

// --- open the Pixel Gradient buffer ---

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

if ( ! frame->Buf_Filter ) {

          SliceO_Buffer *buf_filter = frame->User_Get( ID_FILTER ) ;

          if ( ! buf_filter ) {

                    // --- if the buffer does not exist, then the mask must be cleared! ---

                    for ( int j=0; j < frame->m_y; j++ )

                              for ( int i=0; i < frame->m_x; i++ )

                                        mask[j][i] &= ~PIXEL_MASK_FILTER ;

                    // --- prepare a buffer in the frame's user memory ---

                    buf_filter = new SliceO_Buffer( PRIORITY_FILTER ) ;

                    MEM_CHECK( buf_filter ) ;

                    buf_filter->Name_Set( "Pixel: Filtered" ) ;

                    if ( ! buf_filter->Matrix_New( frame->m_x, frame->m_y, sizeof(unsigned short) ) )

                              return( 0 ) ;

                    frame->User_Set( ID_FILTER, buf_gradient ) ;

          }

          frame->Buf_Filter = (void *) buf_filter->Matrix_Get() ;

}

unsigned short **gradient = (int **) frame->Buf_Filter ;

 

 

The Gradient buffer

 

Unless you plan to replace the default callbacks for "Fct_Pixel_Get_Gradient_Amp" and "Fct_Pixel_Get_Gradient_Dir", this buffer is of no interest to you.  This buffer contain the gradient amplitude and direction for each of the GLI pixels.  The gradient amplitude is stored as a 16 bit unsigned short in the higher 16 bits of a 32 bit value.  The lower 16 bits store the direction of the gradient as 2 angles in spherical systems.

 

If you have "frame" a pointer to a frame, you can get a pointer to the Gradient buffer with:

 

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

// --- open the Pixel Gradient buffer ---

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

if ( ! frame->Buf_Gradient ) {

          SliceO_Buffer *buf_gradient = frame->User_Get( ID_GRADIENT ) ;

          if ( ! buf_gradient ) {

                    // --- if the buffer does not exist, then the mask must be cleared! ---

                    for ( int j=0; j < frame->m_y; j++ )

                              for ( int i=0; i < frame->m_x; i++ )

                                        mask[j][i] &= ~PIXEL_MASK_GRAD ;

                    // --- prepare a buffer in the frame's user memory ---

                    buf_gradient = new SliceO_Buffer( PRIORITY_GRADIENT ) ;

                    MEM_CHECK( buf_gradient ) ;

                    buf_gradient->Name_Set( "Pixel: Gradient" ) ;

                    if ( ! buf_gradient->Matrix_New( frame->m_x, frame->m_y, sizeof(int) ) )

                              return( 0 ) ;

                    frame->User_Set( ID_GRADIENT, buf_gradient ) ;

          }

          frame->Buf_Gradient = (void *) buf_gradient->Matrix_Get() ;

}

int **gradient = (int **) frame->Buf_Gradient ;