function varargout = imcontrast(handle, varargin)
%IMCONTRAST  Adjust contrast tool.
%   IMCONTRAST creates an Adjust Contrast tool associated with the intensity
%   image in the current figure, called the target image. If the figure
%   contains multiple images, the target image is the first intensity image in
%   the figure (last created). IMCONTRAST creates the tool in a separate figure
%   window.
%
%   The Adjust Contrast tool is an interactive contrast and brightness
%   adjustment tool. When an image contains more gray values than can be
%   displayed at one time, you can use this tool to choose which values to
%   display. For images with limited contrast, you can use this tool to
%   stretch the image histogram to fill the dynamic range.
%
%   Note: The Adjust Contrast tool only works with intensity (grayscale)
%   images. IMCONTRAST adjusts the CLim property of the axes containing the
%   image and does not work with RGB images. Changing the CLim of indexed
%   images may produce unexpected results.
%
%   IMCONTRAST(H) creates an Adjust Contrast tool associated with the image
%   specified by the handle H. H may be an image, axes, uipanel, or figure
%   handle. If H is an axes or figure handle, IMCONTRAST uses the first
%   image found in the axes or figure.
%
%   H = IMCONTRAST(...) returns a handle to the Adjust Contrast tool figure.
%
%   Remarks
%   -------
%   The Adjust Contrast tool presents a scaled histogram of pixel values (overly
%   represented pixel values are truncated for clarity). Dragging on the left
%   red bar in the histogram display changes the minimum value. The minimum
%   value (and any value less than the minimum) displays as black. Dragging on
%   the right red bar in the histogram changes the maximum value. The maximum
%   value (and any value greater than the maximum) displays as white. Values in
%   between the red bars display as intermediate shades of gray.
%
%   Together the minimum and maximum values create a "window". Stretching the
%   window reduces contrast. Shrinking the window increases contrast. Changing
%   the center of the window changes the brightness of the image. It is
%   possible to manually enter the minimum, maximum, width, and center values
%   for the window. Changing one value automatically updates the other values
%   and the image.
%
%   Window/Level Interactivity
%   --------------------------
%   Clicking and dragging the mouse within the target image interactively
%   changes the image's window values. Dragging the mouse horizontally from
%   left to right changes the window width (i.e., contrast). Dragging the mouse
%   vertically up and down changes the window center (i.e., brightness).
%   Holding down the CTRL key when clicking accelerates changes. Holding down
%   the SHIFT key slows the rate of change. Keys must be pressed before clicking
%   and dragging.
%
%   Example
%   -------
%
%       imshow('pout.tif')
%       imcontrast(gca)
%
%    See also IMADJUST, IMTOOL, STRETCHLIM.

%   Copyright 1993-2004 The MathWorks, Inc.
%   $Revision: 1.1.8.2 $  $Date: 2004/12/18 07:36:05 $


% Do sanity checking on handles and take care of the zero-argument case.

eid = sprintf('Images:%s:noImageInFigure',mfilename);
if (nargin == 0)
   handle = get(0,'CurrentFigure');
   if isempty(handle)
      msg = sprintf('%s expects a current figure containing an image.', ...
         upper(mfilename));
      error(eid,'%s',msg);
   end
end

iptcheckhandle(handle, {'figure', 'axes', 'image','uipanel'}, mfilename, ...
   'H', 1);

[imageHandle, axHandle, figHandle] = imhandles(handle);

if (isempty(imageHandle))
   error(eid, 'The figure must contain at least one image.')
end

% IMCONTRAST uses the first image if there are multiple.
imageHandle = imageHandle(1);
axHandle = ancestor(imageHandle, 'axes');

% Display the original image.
% - Use a DRAWNOW to work around a bug that forces imcontrast to the
%   background (geck #219657).
figure(figHandle); drawnow

% Open a new figure or bring up an existing one?
hFig = getappdata(axHandle, 'imcontrastFig');
if (~isempty(hFig))

   figure(hFig);

   if (nargout > 0)
      varargout{1} = hFig;
      return
   end

end

% Verify that the image type is supported.
if (~isSupportedImage(imageHandle))

   error('Images:imcontrast:unsupportedImageType', ...
      'Only intensity images are supported.')

end

% Create the histogram palette.
hHistFig = histogramPalette(imageHandle);

if (nargout > 2)

   error('Images:imcontrast:tooManyOutputs', ...
      'Zero or one output arguments expected.');

elseif (nargout == 1)

   varargout{1} = hHistFig;

end

end


function hFig = histogramPalette(imageHandle)

[hImageIm, hImageAx, hImageFig] = imhandles(imageHandle);
X = get(imageHandle, 'CData');

% By default, round editbox values, and display with no decimal
% places except for the center (which can be halfway between two
% integers).
editFormatFcn =   @(value) sprintf('%0.0f', value);
centerFormatFcn = @(value) sprintf('%0.0f', value);
valueFormatter = @round;
wlMotionScale = 1;
isDoubleData = false;

xMin = min(X(:));
xMax = max(X(:));

% Compute Histogram for the image.
switch (class(X))
   case 'uint8'

      nbins = 256;

      [counts, bins] = imhist(X, nbins);
      finalBins = bins;

   case {'int16', 'uint16'}

      nbins = 65536/4;
      wlMotionScale = 4;

      [counts, bins] = imhist(X, nbins);
      finalBins = bins;

   case {'double'}

      % Images with double CData often don't work well with IMHIST.
      % Convert all images to be in the range [0,1] and convert back
      % later if necessary.
      if (xMin >= 0) && (xMax <= 1)

         nbins = 256;

         % Don't round values and use 4 places of precision for all values.
         valueFormatter = @(x) x;
         editFormatFcn = @(value) sprintf('%0.4f', value);
         centerFormatFcn = editFormatFcn;
         wlMotionScale = 1/255;
         isDoubleData = true;

      else

         if ((xMax - xMin) > 1023)

            wlMotionScale = 4;
            nbins = 1024;

         elseif ((xMax - xMin) > 255)

            wlMotionScale = 2;
            nbins = 256;

         else

            nbins = xMax - xMin + 1;

         end

         X = mat2gray(X);

      end

      [counts, bins] = imhist(X, nbins); %bins is in range [0,1]
      finalBins = round(bins .* (xMax - xMin) + xMin); %finalBins is in range
      %of original data.

   otherwise

      error('Images:imcontrast:classNotSupported', ...
         'This image class is not yet supported.')

end

%cannot use xMin or xMax because X changed for the double images in
%non-default range of [0,1]
idx = find((bins >= min(X(:))) & (bins <= max(X(:))));
mu = mean(counts(idx));
counts(counts > (8 * mu)) = 8 * mu;

% Create the histogram panel.
hFig = figure('visible', 'off', ...
   'toolbar', 'none', ...
   'menubar', 'none', ...
   'IntegerHandle', 'off', ...
   'NumberTitle', 'off', ...
   'Name', 'Contrast Adjustment Tool', ...
   'tag', 'imcontrast', ...
   'HandleVisibility', 'callback', ...
   'units', 'pixels', ...
   'renderer', 'zbuffer', ...  % Work around a patch clipping bug.
   'position', [0 0 560 300]);

set(hFig, 'Units', 'normalized');

createMenubar;

iptwindowalign(hImageFig, 'left', hFig, 'left');
iptwindowalign(hImageFig, 'bottom', hFig, 'top');

hPanelHist = uipanel('parent', hFig, ...
   'units', 'normalized', ...
   'position', [0.0, 0.08, 1.0, 0.52]);

hAx = axes('parent', hPanelHist);

switch (nbins)
   case 256

      hStem = stem(hAx, finalBins, counts);

      switch (class(X))
         case {'double', 'single'}

            minX = 0;
            maxX = 1;

         otherwise

            minX = 0;
            maxX = 255;

      end

      setappdata(hFig, 'bitdepth', 8);

   case {1024, 65536/4}

      hStem = stem(hAx, finalBins(idx), counts(idx));
      minX = finalBins(idx(1)) - 100;
      maxX = finalBins(idx(end)) + 100;

      setappdata(hFig, 'bitdepth', 16);

   otherwise

      % For unusual images with a limited dynamic range and few
      % distinct values, display 256 gray levels.
      hStem = stem(hAx, finalBins(idx), counts(idx));

      if (xMin < 0)

         minX = min(xMin, -128);
         maxX = minX + 255;

      else

         maxX = max(xMax, 255);
         minX = maxX - 255;

      end

      setappdata(hFig, 'bitdepth', 8);

end

maxY = max(counts);

set(hStem, 'marker', 'none')
set(hAx, 'ytick', [])
axis(hAx, [minX, maxX, 0, maxY])

setappdata(hAx, 'HistXLim', [minX maxX]);

%
% Add the CLim window to the histogram panel.
%

% If the clim is already in the region, use those values.
origCLim = get(hImageAx, 'clim');
newCLim = [minX maxX];

if (origCLim(1) > minX)
   newCLim(1) = origCLim(1);
end

if (origCLim(2) < maxX)
   newCLim(2) = origCLim(2);
end

% Draw the current CLIM window behind the histogram.
hPatch = patch([newCLim(1) newCLim(1) newCLim(2) newCLim(2)], ...
   [0 maxY maxY 0], [1 0.8 0.8], ...
   'parent', hAx, 'zData', [-2 -2 -2 -2], ...
   'tag', 'WindowPatch', ...
   'ButtonDownFcn', @patchButtonDown);

hMinLine = line('parent', hAx, 'tag', 'MinLine', ...
   'xdata', [newCLim(1) newCLim(1)], 'ydata', [0 maxY], ...
   'ZData', [-1 -1], ...
   'color', [1 0 0], ...
   'LineWidth', 1, ...
   'ButtonDownFcn', @minLineButtonDown);

XLim = get(hAx, 'XLim');
YLim = get(hAx, 'YLim');

hMaxLine = line('parent', hAx, 'tag', 'MaxLine', ...
   'xdata', [newCLim(2) newCLim(2)], 'ydata', [0 maxY], ...
   'ZData', [-1 -1], ...
   'color', [1 0 0], ...
   'LineWidth', 1, ...
   'ButtonDownFcn', @maxLineButtonDown);

[width, center] = computeWindow(newCLim);
hCenterLine = line('parent', hAx, 'tag', 'CenterLine', ...
   'xdata', [center center], 'ydata', [0 maxY], ...
   'zdata', [-2 -2], ...
   'color', [1 0 0], ...
   'LineWidth', 1, ...
   'LineStyle', '--', ...
   'ButtonDownFcn', @patchButtonDown);

% Add handles to make moving the endpoints easier for very small windows.
[XShape, YShape] = getSidePatchShape;
hMinPatch = patch('parent', hAx, ...
   'XData', newCLim(1) - (XShape * (XLim(2) - XLim(1))), ...
   'YData', YShape * YLim(2), ...
   'ZData', ones(size(XShape)), ...
   'FaceColor', [1 0 0], ...
   'EdgeColor', [1 0 0], ...
   'tag', 'MinPatch', ...
   'ButtonDownFcn', @minPatchDown);

hMaxPatch = patch('parent', hAx, ...
   'XData', newCLim(2) + (XShape * (XLim(2) - XLim(1))), ...
   'YData', YShape * YLim(2), ...
   'ZData', ones(size(XShape)), ...
   'FaceColor', [1 0 0], ...
   'EdgeColor', [1 0 0], ...
   'tag', 'MaxPatch', ...
   'ButtonDownFcn', @maxPatchDown);

[XShape, YShape] = getTopPatchShape;
hCenterPatch = patch('parent', hAx, ...
   'XData', center + XShape .* (XLim(2) - XLim(1)), ...
   'YData', YShape * (YLim(2) - YLim(1)), ...
   'ZData', ones(size(XShape)), ...
   'FaceColor', [1 0 0], ...
   'EdgeColor', [1 0 0], ...
   'tag', 'CenterPatch', ...
   'ButtonDownFcn', @patchButtonDown);

%
% Create the window center/width and clipping panel.
%

hWindowClipPanel = uipanel('parent', hFig, ...
   'units', 'normalized', ...
   'position', [0.0, 0.6, 1.0, 0.4]);

figColor = get(hWindowClipPanel, 'BackgroundColor');

set(hWindowClipPanel, 'units', 'characters');

labelLeft = 0;
editLeft = 0;

hAutoButton = uicontrol('Parent', hWindowClipPanel, ...
   'Units', 'characters', ...
   'Style', 'Pushbutton', ...
   'Tag', 'AutoButton', ...
   'HorizontalAlignment', 'center', ...
   'BackgroundColor', figColor, ...
   'String', 'Auto Scale...', ...
   'TooltipString', 'Automatically adjust the brightness and contrast', ...
   'callback', @autoscale);

hResetButton = uicontrol('Parent', hWindowClipPanel, ...
   'Units', 'characters', ...
   'Style', 'Pushbutton', ...
   'Tag', 'ResetButton', ...
   'HorizontalAlignment', 'center', ...
   'BackgroundColor', figColor, ...
   'String', 'Reset Image', ...
   'TooltipString', 'Reset to original values', ...
   'callback', @resetInitial);

hMinLabel = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'style', 'text', ...
   'string', 'Minimum Value', ...
   'HorizontalAlignment', 'left', ...
   'BackgroundColor', figColor);

hMinEdit = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'Style', 'Edit', ...
   'Tag', 'MinEdit', ...
   'HorizontalAlignment', 'right', ...
   'BackgroundColor', [1 1 1], ...
   'TooltipString', 'The window''s minimum intensity value', ...
   'callback', @editboxCallback');

iconRoot = ipticondir;

iconCdata = makeToolbarIconFromPNG(fullfile(iconRoot, ...
   'tool_eyedropper_black.png'));

hMinDropper = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'style', 'pushbutton', ...
   'cdata', iconCdata, ...
   'TooltipString', 'Select minimum window value from image', ...
   'tag', 'MinDropper', ...
   'HorizontalAlignment', 'center', ...
   'callback', @eyedropper);

hMaxLabel = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'style', 'text', ...
   'string', 'Maximum Value', ...
   'HorizontalAlignment', 'left', ...
   'BackgroundColor', figColor);

hMaxEdit = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'Style', 'Edit', ...
   'Tag', 'MaxEdit', ...
   'HorizontalAlignment', 'right', ...
   'BackgroundColor', [1 1 1], ...
   'TooltipString', 'The window''s maximum intensity value', ...
   'callback', @editboxCallback');

iconCdata = makeToolbarIconFromPNG(fullfile(iconRoot, ...
   'tool_eyedropper_white.png'));

hMaxDropper = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'style', 'pushbutton', ...
   'cdata', iconCdata, ...
   'TooltipString', 'Select maximum window value from image', ...
   'tag', 'MaxDropper', ...
   'HorizontalAlignment', 'center', ...
   'callback', @eyedropper);

hWidthLabel = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'style', 'text', ...
   'string', 'Window Width', ...
   'HorizontalAlignment', 'left', ...
   'BackgroundColor', figColor);

hWidthEdit = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'Style', 'Edit', ...
   'Tag', 'WidthEdit', ...
   'HorizontalAlignment', 'right', ...
   'BackgroundColor', [1 1 1], ...
   'TooltipString', 'The width of the intensity window', ...
   'callback', @editboxCallback');

hCenterLabel = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'style', 'text', ...
   'string', 'Window Center', ...
   'HorizontalAlignment', 'left', ...
   'BackgroundColor', figColor);

hCenterEdit = uicontrol('parent', hWindowClipPanel, ...
   'units', 'characters', ...
   'Style', 'Edit', ...
   'Tag', 'CenterEdit', ...
   'HorizontalAlignment', 'right', ...
   'BackgroundColor', [1 1 1], ...
   'TooltipString', 'The center of the intensity window', ...
   'callback', @editboxCallback');

%
% Position all of the uicontrols.
%

% First find the maximum extents of the labels.
labelExtents = get([hMaxLabel, hMinLabel, hCenterLabel, hWidthLabel, ...
   hMinDropper, hMaxDropper], ...
   'extent');
allExtents = [labelExtents{:}];
maxExtentLength = max(allExtents(3:4:end));
maxExtentHeight = max(allExtents(4:4:end));

% Next, align the midlines of the edit box and labels.
editboxHeight = 1.5;

bottomOffset = 1.5/2 - maxExtentHeight/2;

buttonSize = maxExtentHeight;

% Set the positions.
set(hMaxLabel, 'position', [1, 1 - 2*bottomOffset, ...
   maxExtentLength, maxExtentHeight]);
set(hMinLabel, 'position', [1, 2 - 2*bottomOffset + 1.5, ...
   maxExtentLength, maxExtentHeight]);
set(hMaxEdit,  'position', [1 + maxExtentLength, 1, ...
   8, 1.5]);
set(hMinEdit,  'position', [1 + maxExtentLength, 2 + 1.5, ...
   8, 1.5]);

set(hMaxDropper, 'position', [maxExtentLength + 11, 1, ...
   buttonSize,  buttonSize]);
set(hMaxDropper, 'units', 'pixels');
pos = get(hMaxDropper, 'position');
set(hMaxDropper, 'position', [pos(1) pos(2) 20 20]);
set(hMaxDropper, 'units', 'characters');

set(hMinDropper, 'position', [maxExtentLength + 11, 2 + 1.5, ...
   buttonSize,  buttonSize]);
set(hMinDropper, 'units', 'pixels');
pos = get(hMinDropper, 'position');
set(hMinDropper, 'position', [pos(1) pos(2) 20 20]);
set(hMinDropper, 'units', 'characters');


set(hCenterLabel, 'position', [maxExtentLength + 13 + 2*buttonSize + 1, 1 - 2*bottomOffset, ...
   maxExtentLength, maxExtentHeight]);
set(hWidthLabel,  'position', [maxExtentLength + 13 + 2*buttonSize + 1, 2 - 2*bottomOffset + 1.5, ...
   maxExtentLength, maxExtentHeight]);
set(hCenterEdit,  'position', [2*maxExtentLength + 13 + 2*buttonSize + 1, 1, ...
   8, 1.5]);
set(hWidthEdit,   'position', [2*maxExtentLength + 13 + 2*buttonSize + 1, 2 + 1.5, ...
   8, 1.5]);

p = get(hAutoButton, 'position');
buttonExtent = get(hAutoButton, 'extent');
set(hAutoButton,  'position', [1, 6, buttonExtent(3) + 2, p(4)]);

p = get(hAutoButton, 'position');
set(hResetButton, 'position', [3 + p(3), 6, p(3), p(4)]);


%
% Create the status panel.
%

hStatusPanel = uipanel('Parent', hFig, ...
   'Units', 'normalized', ...
   'Position', [0.0, 0.0, 1.0, 0.08], ...
   'BorderType', 'none');

defaultMessage = 'Adjust the histogram above, or click and drag the mouse over the image.';

hStatusLabel = uicontrol('parent', hStatusPanel, ...
   'units', 'normalized', ...
   'position', [0 0 1 1], ...
   'style', 'text', ...
   'HorizontalAlignment', 'left', ...
   'string', defaultMessage);

% Position the status string.
set(hStatusPanel, 'units', 'pixel');
set(hStatusLabel, 'units', 'pixel');

panelExtent = get(hStatusPanel, 'position');
labelExtent = get(hStatusLabel, 'extent');
set(hStatusLabel, 'Position', [0, ...
   (panelExtent(4) - labelExtent(4)) ./ 2, ...
   panelExtent(3), ...
   labelExtent(4)]);

set(hStatusPanel, 'units', 'normalized');
set(hStatusLabel, 'units', 'normalized');


%
% Set the values and get ready to go.
%

resetEditValues;
setappdata(hFig, 'InitialWindow', newCLim);
set(hWindowClipPanel, 'units', 'normalized');

% Close the histogram palette if the image goes away.
childRemovedListener = handle.listener(hImageAx,'ObjectChildRemoved', ...
   {@childRemoved, hFig});

% Update the window if the CLIM changes from outside the tool.
%axesHandle = handle(hImageAx);
%clim = axesHandle.findprop('CLim');
%ClimListener = handle.listener(axesHandle, clim, ...
%                               'PropertyPostSet', @updater);

% INKY SAYS STORE IN APPDATA.

setappdata(hImageAx, 'imcontrastFig', hFig);
setappdata(hFig, 'SamplerNeighborhood', 1);
setappdata(hAx, 'AllowNudging', false);

% Attach window/level mouse actions.
attachWLAction(imageHandle, hFig);
imageOrigPointer = get(hImFig,'Pointer');
imageOrigPointerShapeCData = get(hImFig,'PointerShapeCData');
set(hImFig, 'Pointer', 'Custom');
set(hImFig, 'PointerShapeCData', getWLPointer);

% Display the figure.
set(hFig, 'visible', 'on');


   %---------------------
   function createMenubar

      filemenu = uimenu(hFig,'Label','&File','Tag','file menu');

      if isJavaFigure
         uimenu(hFig,'Label','&Window','tag','winmenu',...
            'Callback',winmenu('callback'));
      end

      % File menu
      uimenu(filemenu,'Label','&Close','Accelerator','W',...
         'Callback',@(varargin) close(hFig));

      % Help menu
      if ~isdeployed
         helpmenu = uimenu(hFig,'Label','&Help','Tag','help menu');
         uimenu(helpmenu,'Label','Contrast Adjustment Help','Tag',...
            'imcontrast help menu','Callback',...
            @(varargin) ipthelp('imtool_imagecontrast_help','Contrast Adjustment'));
         iptstandardhelp(helpmenu);
      end

   end

   % ----------------------------
   function resetInitial(varargin)

      updater(varargin{1}, [], getappdata(hFig, 'InitialWindow'));

   end


   function autoscale(varargin)

      % Put up a dialog to pick the autoscale mode.
      hAutoScaleFig = figure('toolbar', 'none', ...
         'visible', 'off', ...
         'menubar', 'none', ...
         'IntegerHandle', 'off', ...
         'NumberTitle', 'off', ...
         'Name', 'Auto Scale Window', ...
         'tag', 'AutoScaleDialog', ...
         'HandleVisibility', 'callback', ...
         'units', 'char', ...
         'position', [0 0 50 10], ...
         'windowstyle', 'modal');

      bgColor = get(hAutoScaleFig, 'Color');

      % Layout the controls.
      msg = {'Automatically set window width and center', ...
         'using minimum and maximum intensity values.'};

      hHeadline = uicontrol('parent', hAutoScaleFig, ...
         'style', 'text', ...
         'BackgroundColor', bgColor, ...
         'string', msg, ...
         'units', 'char', ...
         'HorizontalAlignment', 'left', ...
         'position', [1 6.5 48 3]);

      hCheckbox = uicontrol('parent', hAutoScaleFig, ...
         'style', 'checkbox', ...
         'BackgroundColor', bgColor, ...
         'max', true, 'min', false, ...
         'units', 'char', ...
         'string', 'Eliminate outliers: ', ...
         'value', true, ...
         'callback', @checkboxToggle);

      p = get(hCheckbox, 'position');
      set(hCheckbox, 'position', [1 5 21 p(4)]);

      hPctEdit = uicontrol('parent', hAutoScaleFig, ...
         'style', 'edit', ...
         'BackgroundColor', [1 1 1], ...
         'string', '2', ...
         'units', 'char');

      p1 = get(hCheckbox, 'position');
      p2 = get(hPctEdit, 'position');
      set(hPctEdit, 'position', [p1(1) + p1(3), p1(2), p2(3), p2(4)]);

      hPctLabel = uicontrol('parent', hAutoScaleFig, ...
         'style', 'text', ...
         'BackgroundColor', bgColor, ...
         'units', 'char', ...
         'string', '%', ...
         'horizontalAlignment', 'left');

      p1 = get(hPctEdit, 'position');
      p2 = get(hPctLabel, 'position');
      set(hPctLabel, 'position', [p1(1) + p(3), p1(2), p2(3), p2(4)]);

      hOkButton = uicontrol('parent', hAutoScaleFig, ...
         'style', 'pushbutton', ...
         'units', 'char', ...
         'string', 'OK', ...
         'callback', @autoscaleOk);

      hCancelButton = uicontrol('parent', hAutoScaleFig, ...
         'style', 'pushbutton', ...
         'units', 'char', ...
         'string', 'Cancel', ...
         'callback', @autoscaleClose);

      p = get(hOkButton, 'position');
      set(hOkButton, 'position', [25 - p(3) - 2, 1, p(3), p(4)]);
      set(hCancelButton, 'position', [25 + 2, 1, p(3), p(4)]);

      % Place the figure over the middle of the imcontrast tool.
      iptwindowalign(hFig, 'hcenter', hAutoScaleFig, 'hcenter');
      iptwindowalign(hFig, 'vcenter', hAutoScaleFig, 'vcenter');

      set(hAutoScaleFig, 'visible', 'on')

      % Wait for the user to do something.
      uiwait(hAutoScaleFig)


      function checkboxToggle(varargin)

         if (get(hCheckbox, 'value'))
            set(hPctEdit, 'enable', 'on');
         else
            set(hPctEdit, 'enable', 'off');
         end

      end


      function autoscaleOk(varargin)

         % Verify the percent and use it if box is checked.
         outlierPct = getEditValue(hPctEdit);
         removeOutliers = get(hCheckbox, 'value');

         if ((isempty(outlierPct)) || ...
               ((outlierPct > 100) || (outlierPct < 0)))

            errordlg({'Percentage must be a number between 0 and 100.'}, ...
               'Invalid percentage', ...
               'modal')

            set(hPctEdit, 'string', '2');
            return

         elseif (removeOutliers)

            outlierPct = outlierPct ./ 100;

         else

            outlierPct = 0;

         end

         % Find the new window endpoints.
         CData = get(imageHandle, 'CData');
         CDataType = class(CData);

         % Double image data must be scaled and shifted to the range [0,1]
         % for STRETCHLIM to do the right thing.
         if (isequal(CDataType, 'double'))

             % Keep track of old CData range for reconversion.
             minCData = min(CData(:));
             maxCData = max(CData(:));
 
             CData = mat2gray(CData);
            
         end

         newCLim = stretchlim(CData, outlierPct/2);

         if (removeOutliers && isequal(newCLim, [0; 1]))
           
             errordlg({'The specified percentage is too great.', ...
                       'Choose a smaller percentage.'}, ...
                      'Percentage too large', ...
                      'modal')
             
             return
             
         end

         % Scale the CLim from STRETCHLIM's [0,1] to match the range
         % of the data.
         switch (CDataType)
            case {'int8', 'uint8'}
               newCLim = 255 .* newCLim;

            case {'int16', 'uint16'}
               newCLim = 65535 .* newCLim;

            case {'double'}
               newCLim = newCLim .* (maxCData - minCData);
               newCLim = newCLim + minCData;

         end

         % Update the image, histogram, etc., and close.
         updater(hAutoButton, [], newCLim);
         autoscaleClose(varargin{:});

      end


      function autoscaleClose(varargin)
         delete(hAutoScaleFig)
      end


   end


   function editboxCallback(varargin)

      minValue = getEditValue(hMinEdit);
      maxValue = getEditValue(hMaxEdit);
      centerValue = getEditValue(hCenterEdit);
      widthValue = getEditValue(hWidthEdit);

      % Validate data.
      % - If invalid: display dialog, reset to last good value, stop.
      % - If valid: go to other callback processor.
      if (any([isempty(minValue), ...
            isempty(maxValue), ...
            isempty(widthValue), ...
            isempty(centerValue)]))

         errordlg({'Edit box values must be numeric values.'}, ...
            'Invalid edit value', ...
            'modal')

         resetEditValues;
         return;

      elseif (minValue >= maxValue)

         errordlg({'Minimum value must be less than maximum value.'}, ...
            'Invalid edit value', ...
            'modal')

         resetEditValues;
         return;

      elseif (((widthValue < 1) && (~isDoubleData)) || ...
            (widthValue <= 0))

         errordlg({'Window width must be greater than zero.'}, ...
            'Invalid edit value', ...
            'modal')

         resetEditValues;
         return;

      elseif ((floor(centerValue * 2) ~= centerValue * 2) && (~isDoubleData))

         errordlg({'Invalid window center value.'}, ...
            'Invalid edit value', ...
            'modal')

         resetEditValues;
         return;

      end

      % Values are acceptable.
      updater(varargin{:})

   end


   function updater(varargin)

      if (nargin == 3)

         newCLim = varargin{end};
         newMin = newCLim(1);
         newMax = newCLim(2);
         
      end

      % What triggered the update?
      if (isa(varargin{1}, 'schema.prop'))

          component = 'CData';
          
      else
        
          component = get(varargin{1}, 'tag');
          
      end

      % Determine new endpoints.
      switch (component)
         case 'CData'
        
            newCData = get(varargin{2}, 'NewValue');
            newMin = newCData(1);
            newMax = newCData(2);
            
         case {'CenterEdit'}

            centerValue = getEditValue(hCenterEdit);
            widthValue = getEditValue(hWidthEdit);
            [newMin, newMax] = computeCLim(widthValue, centerValue);

            if ((newMin < XLim(1)) && (newMax > XLim(2)))

               newMin = XLim(1);
               newMax = XLim(2);

            elseif (newMin < XLim(1))

               newMin = XLim(1);
               newMax = newMin + 2 * (centerValue - newMin);

            elseif (newMax > XLim(2))

               newMax = XLim(2);
               newMin = newMax - 2 * (newMax - centerValue);

            end


         case {'WidthEdit'}

            centerValue = getEditValue(hCenterEdit);
            widthValue = getEditValue(hWidthEdit);
            [newMin, newMax] = computeCLim(widthValue, centerValue);

            if ((newMin < XLim(1)) && (newMax > XLim(2)))

               newMin = XLim(1);
               newMax = XLim(2);

            elseif (newMin < XLim(1))

               newMin = XLim(1);
               newMax = newMin + widthValue;

            elseif (newMax > XLim(2))

               newMax = XLim(2);
               newMin = newMax - widthValue;

            end

         case {'MinEdit', 'MaxEdit', 'MinDropper', 'MaxDropper'}

            newMin = getEditValue(hMinEdit);
            newMax = getEditValue(hMaxEdit);

         case {'MinLine', 'MaxLine', 'CenterLine', 'MinPatch', 'MaxPatch'}

            minXData = get(hMinLine, 'XData');
            newMin = minXData(1);
            maxXData = get(hMaxLine, 'XData');
            newMax = maxXData(1);

         case {'WindowPatch'}

            origWidth = getEditValue(hWidthEdit);

            minXData = get(hMinLine, 'XData');
            newMin = minXData(1);
            maxXData = get(hMaxLine, 'XData');
            newMax = maxXData(1);

         case {'AutoButton', 'ResetButton'}

            newCLim = varargin{end};
            newMin = newCLim(1);
            newMax = newCLim(2);

         case {'imcontrast'}

            % Why does this code path get called?
            if (nargin ~= 3)

               newCLim = get(hImageAx, 'CLim');
               newMin = newCLim(1);
               newMax = newCLim(2);

            end

         otherwise

            errordlg({'You have updated the contrast/brightness in an unexpected way.'}, ...
               'Unexpected update', ...
               'modal')

            resetEditValues;
            return;

      end

      % Prevent new endpoints from exceeding visible min or max.
      % Don't let window shrink when dragging the window patch.
      if (newMin < XLim(1))

         if (isequal(component, 'WindowPatch'))
            newMin = XLim(1);
            newMax = newMin + origWidth;
         else
            newMin = XLim(1);
         end

      end

      if (newMax > XLim(2))

         if (isequal(component, 'WindowPatch'))
            newMax = XLim(2);
            newMin = newMax - origWidth;
         else
            newMax = XLim(2);
         end

      end

      % Keep min < max
      if (((newMax - 1) < newMin) && (~isDoubleData))

         if (getappdata(hFig, 'allowNudging'))

            % Nudge one of the values.
            if (isequal(component, 'MinLine') || (newMax == XLim(1)))
               % The min line was moved or the max line was moved to the min.
               newMax = newMin + 1;
            else
               % The max line was moved or the min line was moved to the max.
               newMin = newMax - 1;
            end

         else

            % Stop at limiting value.
            CLim = get(hImageAx, 'CLim');
            newMin = CLim(1);
            newMax = CLim(2);

         end

         %Made this less than or equal to as a possible workaround to g226780
      elseif ((newMax <= newMin) && (isDoubleData))

         % Stop at limiting value.
         CLim = get(hImageAx, 'CLim');
         newMin = CLim(1);
         newMax = CLim(2);
      end

      % Update edit boxes with new values.
      set(hMinEdit, 'String', editFormatFcn(newMin));
      set(hMaxEdit, 'String', editFormatFcn(newMax));
      [width, center] = computeWindow([newMin newMax]);
      set(hWidthEdit, 'string', editFormatFcn(width));
      if (floor(center) == center)
         set(hCenterEdit, 'String', centerFormatFcn(center));
      else
         set(hCenterEdit, 'String', centerFormatFcn(center));
      end

      % Update patch display.
      updateHistogram('endpoints', [newMin newMax]);

      % Update image CLim.
      updateImage(hImageAx, [newMin newMax]);

   end


   function minLineButtonDown(varargin)
      setappdata(hAx, 'currentLine', hMinLine);
      lineButtonDown(varargin{:});
   end


   function maxLineButtonDown(varargin)
      setappdata(hAx, 'currentLine', hMaxLine);
      lineButtonDown(varargin{:});
   end


   function lineButtonDown(varargin)

      idButtonMotion = iptaddcallback(hFig, 'WindowButtonMotionFcn', @lineMove);
      idButtonUp = iptaddcallback(hFig, 'WindowButtonUpFcn', @lineUp);

      function lineUp(varargin)
         iptremovecallback(hFig, 'WindowButtonMotionFcn', idButtonMotion);
         iptremovecallback(hFig, 'WindowButtonUpFcn', idButtonUp);

         newLim = valueFormatter(get(hImageAx, 'CLim'));
         updater(varargin{1}, [], newLim);

      end

   end


   function lineMove(varargin)

      % Determine which line is being dragged and set appropriate values.
      hLine = getappdata(hAx, 'currentLine');

      cp = get(hAx, 'CurrentPoint');
      xpos = cp(1);

      if (hLine == hMaxLine)
         set(hMaxLine, 'XData', [xpos xpos]);
      elseif (hLine == hMinLine)
         set(hMinLine, 'XData', [xpos xpos]);
      end

      % Update the image, histogram, etc.
      updater(hLine);

   end

   
   function delta = computeMotionDelta(hObject)

         cp = get(hAx, 'CurrentPoint');

         % Don't register changes if the pointer is outside the W/L window.
         if (cp(1) <= XLim(1))
           
             cp(1) = XLim(1);
             
         elseif (cp(2) >= XLim(2))
           
             cp(1) = XLim(2);
             
         end

         % Determine how much to slide the window.
         motionOrigin = getappdata(hObject, 'motionOrigin');
         setappdata(hObject, 'motionOrigin', cp);

         delta = cp(1) - motionOrigin(1);
   
   end

   
   function patchButtonDown(varargin)

      setappdata(hPatch, 'motionOrigin', get(hAx, 'CurrentPoint'));
      setappdata(hAx, 'currentLine', hPatch);

      idButtonMotion = iptaddcallback(hFig, 'windowButtonMotionFcn', @patchMove);
      idButtonUp = iptaddcallback(hFig, 'WindowButtonUpFcn', @patchUp);


      function patchUp(varargin)

         iptremovecallback(hFig, 'WindowButtonMotionFcn', idButtonMotion);
         iptremovecallback(hFig, 'WindowButtonUpFcn', idButtonUp);

         newLim = valueFormatter(get(hImageAx, 'CLim'));
         updater(varargin{1}, [], newLim);

      end


      function patchMove(varargin)

         delta = computeMotionDelta(hPatch);

         % Set the window endpoints.
         set(hMinLine, 'XData', get(hMinLine, 'XData') + delta);
         set(hMaxLine, 'XData', get(hMaxLine, 'XData') + delta);

         updater(hPatch);

      end

   end


   function minPatchDown(varargin)

      setappdata(hMinPatch, 'motionOrigin', get(hAx, 'CurrentPoint'))

      idButtonMotion = iptaddcallback(hFig, 'WindowButtonMotionFcn', @patchMove);
      idButtonUp = iptaddcallback(hFig, 'WindowButtonUpFcn', @patchUp);


      function patchUp(varargin)
         iptremovecallback(hFig, 'WindowButtonMotionFcn', idButtonMotion);
         iptremovecallback(hFig, 'WindowButtonUpFcn', idButtonUp);

         newLim = valueFormatter(get(hImageAx, 'CLim'));
         updater(hMinPatch, [], newLim);

      end


      function patchMove(varargin)

         delta = computeMotionDelta(hMinPatch);

         % Set the window endpoints.
         set(hMinLine, 'XData', get(hMinLine, 'XData') + delta);
         updater(hMinPatch);

      end

   end


   function maxPatchDown(varargin)

      setappdata(hMaxPatch, 'motionOrigin', get(hAx, 'CurrentPoint'))

      idButtonMotion = iptaddcallback(hFig, 'WindowButtonMotionFcn', @patchMove);
      idButtonUp = iptaddcallback(hFig, 'WindowButtonUpFcn', @patchUp);


      function patchUp(varargin)
         iptremovecallback(hFig, 'WindowButtonMotionFcn', idButtonMotion);
         iptremovecallback(hFig, 'WindowButtonUpFcn', idButtonUp);

         newLim = valueFormatter(get(hImageAx, 'CLim'));
         updater(hMaxPatch, [], newLim);

      end


      function patchMove(varargin)

         delta = computeMotionDelta(hMaxPatch);

         % Set the window endpoints.
         set(hMaxLine, 'XData', get(hMaxLine, 'XData') + delta);
         updater(hMaxPatch);

      end

   end


   function updateHistogram(mode, newValues)

      sidePatchXData = getSidePatchShape * getPatchScale;
      topPatchXData = getTopPatchShape * getPatchScale;

      switch (mode)
         case 'endpoints'

            currentMin = newValues(1);
            currentMax = newValues(2);
            [width, center] = computeWindow(newValues);

            set(hMinLine, 'XData', [currentMin currentMin]);
            set(hMaxLine, 'XData', [currentMax currentMax]);
            set(hPatch, 'XData', [currentMin currentMin currentMax currentMax]);
            set(hCenterLine, 'XData', [center center]);
            set(hMinPatch, 'XData', currentMin - sidePatchXData);
            set(hMaxPatch, 'XData', currentMax + sidePatchXData);
            set(hCenterPatch, 'XData', center + topPatchXData);

         case 'delta'

            delta = newValues;
            set(hMinLine, 'XData', get(hMinLine, 'XData') + delta);
            set(hMaxLine, 'XData', get(hMaxLine, 'XData') + delta);
            set(hPatch, 'XData', get(hPatch, 'XData') + delta);
            set(hCenterLine, 'XData', get(hCenterLine, 'XData') + delta);
            set(hMinPatch, 'XData', get(hMinPatch, 'XData') + delta);
            set(hMaxPatch, 'XData', get(hMaxPatch, 'XData') + delta);
            set(hCenterPatch, 'XData', get(hCenterPatch, 'XData') + delta);

      end

   end


   function resetEditValues

      CLim = get(hImageAx, 'CLim');

      [widthValue, centerValue] = computeWindow([CLim(1) CLim(2)]);

      set(hMinEdit, 'string', editFormatFcn(CLim(1)))
      set(hMaxEdit, 'string', editFormatFcn(CLim(2)))
      set(hWidthEdit, 'string', editFormatFcn(widthValue))
      set(hCenterEdit, 'string', centerFormatFcn(centerValue))

   end


   function eyedropper(varargin)

      [hIm, hImAx, hImFig] = imhandles(imageHandle);

      % Who called the function?
      switch (get(varargin{1}, 'tag'))
         case 'MinDropper'
            hBox = hMinEdit;
         case 'MaxDropper'
            hBox = hMaxEdit;
      end

      % Prevent uicontrols from issuing callbacks before dropper is done.
      children = get(hWindowClipPanel, 'Children');
      origEnable = get(children, 'Enable');
      set(children, 'Enable', 'off');

      % W/L mouse action sometimes conflicts afterward.  Turn it off briefly.
      origBDF = get(hIm, 'ButtonDownFcn');
      set(hIm, 'ButtonDownFcn', '');

      % Change the pointer to an eyedropper.
      origPointer = get(hImFig, 'Pointer');
      origPointerShapeCData = get(hImFig, 'PointerShapeCData');
      origPointerShapeHotSpot = get(hImFig, 'PointerShapeHotSpot');
      set(hImFig, 'Pointer', 'Custom');
      set(hImFig, 'PointerShapeCData', getEyedropperPointer);
      set(hImFig, 'PointerShapeHotSpot', getEyedropperHotSpot);

      % Change the status text.
      origMsg = get(hStatusLabel, 'string');
      if (isequal(hBox, hMinEdit))
         dropper = 'minimum';
      else
         dropper = 'maximum';
      end
      newMsg = sprintf('Click on a pixel in the image to set the window''s %s value.', ...
         dropper);
      set(hStatusLabel, 'string', newMsg);

      % Take care to undo all of these actions if the adjustment tool closes.
      origCloseRequestFcn = get(hFig, 'CloseRequestFcn');
      set(hFig, 'CloseRequestFcn', @closeDuringEyedropper)

      % Create an eyedropper tool.
      hInvisibleFig = [];
      nbhd = getSamplerNeighborhood;

      value = graysampler(imageHandle, 'neighborhood', nbhd);


      % Set the edit text box.
      if (~isempty(value))
         set(hBox, 'string', editFormatFcn(value(1)));
         editboxCallback(varargin{:});
      end

      if (ishandle(hFig))
         undoEyedropperChanges;
      end



      % -----------------------------
      function undoEyedropperChanges

         % Change the pointer back.
         set(hImFig, 'Pointer', origPointer);
         set(hImFig, 'PointerShapeCData', origPointerShapeCData);
         set(hImFig, 'PointerShapeHotSpot', origPointerShapeHotSpot);

         % Change the message back.
         set(hStatusLabel, 'string', origMsg);

         % Turn the W/L mouse action back on if necessary.
         set(hIm, 'ButtonDownFcn', origBDF);

         % Reenable other uicontrols.
         for p = 1:numel(origEnable)
            set(children(p), 'Enable', origEnable{p});
         end

      end


      % --------------------------------------
      function closeDuringEyedropper(varargin)

         undoEyedropperChanges

         if (ishandle(hInvisibleFig))
            delete(hInvisibleFig)
         end

         if ((~isempty(origCloseRequestFcn)) && ...
               (~isequal(origCloseRequestFcn, 'closereq')))

            feval(origCloseRequestFcn);

         end

         if (ishandle(hFig))
            delete(hFig)
         end

      end

      % ---------------------------------------
      function value = graysampler(h, varargin)
         %GRAYSAMPLER  Sample a gray value from any window.
         %    VALUE = GRAYSAMPLER(HFIG) returns a grayscale value from an image in
         %    the figure with handle HFIG.  The value will be the intensity of the
         %    pixel under the cursor when a mouse button is pressed.
         %
         %    VALUE = GRAYSAMPLER([]) returns a grayscale value from any of the
         %    images that are open when the function is called.  Images contained
         %    in figures whose HandleVisibility property is set to 'Callback' or
         %    'Off' will not be sampled.
         %
         %    VALUE = GRAYSAMPLER(..., 'Neighborhood', N) find the average pixel
         %    intensity in the N-by-N neighborhood centered about where the mouse
         %    button was pressed.

         figure(hImFig);

         % Set button behavior.
         bdfID = iptaddcallback(hIm, 'ButtonDownFcn', @getSample);

         % Initialize value
         hInvisibleFig = figure('visible', 'off');
         value = [];
         waitfor(hInvisibleFig);


         % --------------------------
         function getSample(varargin)

            currentPoint = round(get(hImAx, 'CurrentPoint'));

            cData = get(hIm, 'CData');

            value = cData(currentPoint(1,2), currentPoint(1,1), :);
            value = value(:)';

            % Find the mean/median of the neighborhood.

            % Unset the button behavior.
            iptremovecallback(hIm, 'ButtonDownFcn', bdfID);

            delete(hInvisibleFig);

         end

      end

   end


   function N = getSamplerNeighborhood

      N = getappdata(hFig, 'SamplerNeighborhood');

   end


   function updateImage(hImage, clim)

      if clim(1) >= clim(2)
         eid = sprintf('Images:%s:internalError',mfilename);
         msg = 'Internal error - clim(1) is >= clim(2).';
         error(eid,'%s',msg);
      end

      set(hImageAx, 'clim', clim)

   end


   function attachWLAction(hImage, hHistFig)

      % Set up to track mouse motion.
      cbidButtonDownFcn = iptaddcallback(hImage, 'ButtonDownFcn', @wldown);
      setappdata(hHistFig, 'cbidButtonDownFcn', cbidButtonDownFcn);

      [hIm, hImAx, hImFig] = imhandles(hImage);

      set(hHistFig, 'DeleteFcn', {@closeHistFig, hImFig, cbidButtonDownFcn});


      function wldown(varargin)

         % Set the mouse event functions.
         cbidMotion = iptaddcallback(hImFig, 'WindowButtonMotionFcn', @wlmove);
         cbidUp = iptaddcallback(hImFig, 'WindowButtonUpFcn', @wlup);

         % Keep track of values needed to adjust window/level.
         lastPointer = get(0, 'PointerLocation');

         % Figure out how quickly to change the window/level based on key
         % modifiers.
         WLSpeed = getWLSpeed(hImFig) * wlMotionScale;

         % Set the pointer to a window/level pointer.
         origPointer = get(hImFig, 'Pointer');
         origPointerShapeCData = get(hImFig, 'PointerShapeCData');
         set(hImFig, 'Pointer', 'Custom');
         set(hImFig, 'PointerShapeCData', getWLPointer);


         function wlup(varargin)

            % Stop tracking mouse motion and button up.
            iptremovecallback(hImFig, 'WindowButtonMotionFcn', cbidMotion);
            iptremovecallback(hImFig, 'WindowButtonUpFcn', cbidUp);

            % Reset the pointer.
            set(hImFig, 'Pointer', origPointer);
            set(hImFig, 'PointerShapeCData', origPointerShapeCData);

            newLim = valueFormatter(get(hImageAx, 'CLim'));
            updater(hMinLine, [], newLim);

         end


         function wlmove(varargin)

            % Find out where the pointer has moved to.
            currentPos = get(0, 'PointerLocation');
            offset = currentPos - lastPointer;
            lastPointer = currentPos;

            % Get previous W/L.
            [windowWidth, windowCenter] = computeWindow(get(hImAx, 'CLim'));

            % Compute new window/level values and CLim endpoints.
            windowWidth = windowWidth + WLSpeed * offset(1);    % Contrast
            windowCenter = windowCenter + WLSpeed * offset(2);  % Brightness

            newCLim = zeros(1,2);
            [newCLim(1), newCLim(2)] = computeCLim(windowWidth, ...
               windowCenter);

            % Set the new window endpoints.
            set(hMinLine, 'XData', [newCLim(1) newCLim(1)]);
            set(hMaxLine, 'XData', [newCLim(2) newCLim(2)]);

            % Update the image, histogram, etc.
            updater(hMinLine);
            return

         end

      end


      function closeHistFig(obj,eventData,hImFig,callbackID)
         dettachWLAction(callbackID);
         rmappdata(hImAx, 'imcontrastFig');
         set(hImFig,'Pointer',imageOrigPointer);
         set(hImFig,'PointerShapeCData',imageOrigPointerShapeCData);
      end


      function dettachWLAction(callbackID)
         iptremovecallback(hImage, 'ButtonDownFcn', callbackID);
      end

   end


   function [xFactor, yFactor] = getPatchScale

      XLim = get(hAx, 'XLim');
      YLim = get(hAx, 'YLim');

      xFactor = XLim(2) - XLim(1);
      yFactor = YLim(2) - YLim(1);

   end


   function value = getEditValue(hEdit)

      if (ishandle(hEdit))
         value = valueFormatter(sscanf(get(hEdit, 'string'), '%f'));
      else
         value = [];
      end

   end
end





function speed = getWLSpeed(hFig)

SelectionType = lower(get(hFig, 'SelectionType'));
%disp(SelectionType)

switch (SelectionType)
   case {'normal', 'open'}
      speed = 1;

   case 'extend'
      speed = 0.5;

   case {'alternate', 'alt'}
      speed = 2;

end

end


function PointerShapeCData = getWLPointer

PointerShapeCData = [ ...
   NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN  2   2   2   2   2
   NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN  2   1   1   1   1   1
   NaN NaN NaN NaN NaN NaN NaN NaN NaN  2   1   2   2   2   2   2
   NaN  1   1   2   2  NaN NaN NaN NaN  2   1  NaN NaN NaN NaN NaN
   1   1   1   2   2   2  NaN NaN  2   1   2  NaN NaN NaN NaN NaN
   1   1   1   2   2   2  NaN NaN  2   1   2  NaN NaN NaN NaN NaN
   1   1   1   2   2   2  NaN  2   1   2  NaN NaN NaN NaN NaN NaN
   NaN  1   1   2   2  NaN NaN  2   1   2  NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN NaN  2   1   2  NaN NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN NaN NaN  2   1   2  NaN NaN NaN  2  NaN NaN NaN
   NaN NaN NaN NaN NaN  2   1   2  NaN NaN  2  NaN  2  NaN  2  NaN
   NaN NaN NaN NaN NaN  2   1   2  NaN NaN NaN  1   1   1  NaN NaN
   NaN NaN NaN NaN NaN  1   2  NaN NaN  2   2   1   2   1   2   2
   2   2   2   2   2   1   2  NaN NaN NaN NaN  1   1   1  NaN NaN
   1   1   1   1   1   2  NaN NaN NaN NaN  2  NaN  2  NaN  2  NaN
   2   2   2   2   2  NaN NaN NaN NaN NaN NaN NaN  2  NaN NaN NaN];

end


function PointerShapeCData = getEyedropperPointer

PointerShapeCData = [ ...
   NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN  1   1   1  NaN
   NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN  1   1   1   1   1
   NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN  1   1   1   1   1
   NaN NaN NaN NaN NaN NaN NaN NaN  1   1   1   1   1   1   1   1
   NaN NaN NaN NaN NaN NaN NaN NaN NaN  1   1   1   1   1   1  NaN
   NaN NaN NaN NaN NaN NaN NaN NaN  1   2   1   1   1  NaN NaN NaN
   NaN NaN NaN NaN NaN NaN NaN  1   2   2   2   1   1  NaN NaN NaN
   NaN NaN NaN NaN NaN NaN  1   2   2   2   1  NaN  1  NaN NaN NaN
   NaN NaN NaN NaN NaN  1   2   2   2   1  NaN NaN NaN NaN NaN NaN
   NaN NaN NaN NaN  1   2   2   2   1  NaN NaN NaN NaN NaN NaN NaN
   NaN NaN NaN  1   2   2   2   1  NaN NaN NaN NaN NaN NaN NaN NaN
   NaN NaN  1   2   2   2   1  NaN NaN NaN NaN NaN NaN NaN NaN NaN
   NaN  1   2   2   2   1  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
   NaN  1   2   2   1  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
   1   2   1   1  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
   NaN  1  NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN];


end


function PointerShapeHotSpot = getEyedropperHotSpot

PointerShapeHotSpot = [16 1];

end


function [minPixel, maxPixel] = computeCLim(width, center)
%FINDWINDOWENDPOINTS   Process window and level values.

minPixel = (center - width/2);
maxPixel = minPixel + width;

end



function [width, center] = computeWindow(CLim)

width = CLim(2) - CLim(1);
center = CLim(1) + width ./ 2;

end



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function childRemoved(obj,eventData,hFig)
% This function is called if any child in any axes in the current figure is
% deleted (e.g., you delete a handle to an image, you call imshow again
% using the same figure, etc.)

if isa(eventData.Child,'image')
   % make sure handle still exists
   if ishandle(hFig)
      delete(hFig);
   end
end

end



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [XData, YData] = getSidePatchShape

XData = [0.00 -0.007 -0.007 0.00 0.01 0.02 0.02 0.01];
YData = [0.40  0.42   0.58  0.60 0.60 0.56 0.44 0.40];

end



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [XData, YData] = getTopPatchShape

XData = [-0.015 0.015 0];
YData = [1 1 0.95];

end



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function tf = isSupportedImage(hIm)

imModel = getimagemodel(hIm);

if (~isequal(getImageType(imModel), 'intensity'))
   tf = false;
   return
end

switch (getClassType(imModel))
   case {'uint8', 'uint16', 'uint32'}
      tf = true;

   case {'int8', 'int16', 'int32'}
      tf = true;

   case {'logical'}
      tf = false;

   case {'double', 'single'}

      tf = true;

   otherwise
      tf = false;

end

end
