820 lines
36 KiB
Mathematica
820 lines
36 KiB
Mathematica
|
function imageData = screencapture(varargin)
|
||
|
% screencapture - get a screen-capture of a figure frame, component handle, or screen area rectangle
|
||
|
%
|
||
|
% ScreenCapture gets a screen-capture of any Matlab GUI handle (including desktop,
|
||
|
% figure, axes, image or uicontrol), or a specified area rectangle located relative to
|
||
|
% the specified handle. Screen area capture is possible by specifying the root (desktop)
|
||
|
% handle (=0). The output can be either to an image file or to a Matlab matrix (useful
|
||
|
% for displaying via imshow() or for further processing) or to the system clipboard.
|
||
|
% This utility also enables adding a toolbar button for easy interactive screen-capture.
|
||
|
%
|
||
|
% Syntax:
|
||
|
% imageData = screencapture(handle, position, target, 'PropName',PropValue, ...)
|
||
|
%
|
||
|
% Input Parameters:
|
||
|
% handle - optional handle to be used for screen-capture origin.
|
||
|
% If empty/unsupplied then current figure (gcf) will be used.
|
||
|
% position - optional position array in pixels: [x,y,width,height].
|
||
|
% If empty/unsupplied then the handle's position vector will be used.
|
||
|
% If both handle and position are empty/unsupplied then the position
|
||
|
% will be retrieved via interactive mouse-selection.
|
||
|
% If handle is an image, then position is in data (not pixel) units, so the
|
||
|
% captured region remains the same after figure/axes resize (like imcrop)
|
||
|
% target - optional filename for storing the screen-capture, or the
|
||
|
% 'clipboard'/'printer' strings.
|
||
|
% If empty/unsupplied then no output to file will be done.
|
||
|
% The file format will be determined from the extension (JPG/PNG/...).
|
||
|
% Supported formats are those supported by the imwrite function.
|
||
|
% 'PropName',PropValue -
|
||
|
% optional list of property pairs (e.g., 'target','myImage.png','pos',[10,20,30,40],'handle',gca)
|
||
|
% PropNames may be abbreviated and are case-insensitive.
|
||
|
% PropNames may also be given in whichever order.
|
||
|
% Supported PropNames are:
|
||
|
% - 'handle' (default: gcf handle)
|
||
|
% - 'position' (default: gcf position array)
|
||
|
% - 'target' (default: '')
|
||
|
% - 'toolbar' (figure handle; default: gcf)
|
||
|
% this adds a screen-capture button to the figure's toolbar
|
||
|
% If this parameter is specified, then no screen-capture
|
||
|
% will take place and the returned imageData will be [].
|
||
|
%
|
||
|
% Output parameters:
|
||
|
% imageData - image data in a format acceptable by the imshow function
|
||
|
% If neither target nor imageData were specified, the user will be
|
||
|
% asked to interactively specify the output file.
|
||
|
%
|
||
|
% Examples:
|
||
|
% imageData = screencapture; % interactively select screen-capture rectangle
|
||
|
% imageData = screencapture(hListbox); % capture image of a uicontrol
|
||
|
% imageData = screencapture(0, [20,30,40,50]); % capture a small desktop region
|
||
|
% imageData = screencapture(gcf,[20,30,40,50]); % capture a small figure region
|
||
|
% imageData = screencapture(gca,[10,20,30,40]); % capture a small axes region
|
||
|
% imshow(imageData); % display the captured image in a matlab figure
|
||
|
% imwrite(imageData,'myImage.png'); % save the captured image to file
|
||
|
% img = imread('cameraman.tif');
|
||
|
% hImg = imshow(img);
|
||
|
% screencapture(hImg,[60,35,140,80]); % capture a region of an image
|
||
|
% screencapture(gcf,[],'myFigure.jpg'); % capture the entire figure into file
|
||
|
% screencapture(gcf,[],'clipboard'); % capture the entire figure into clipboard
|
||
|
% screencapture(gcf,[],'printer'); % print the entire figure
|
||
|
% screencapture('handle',gcf,'target','myFigure.jpg'); % same as previous, save to file
|
||
|
% screencapture('handle',gcf,'target','clipboard'); % same as previous, copy to clipboard
|
||
|
% screencapture('handle',gcf,'target','printer'); % same as previous, send to printer
|
||
|
% screencapture('toolbar',gcf); % adds a screen-capture button to gcf's toolbar
|
||
|
% screencapture('toolbar',[],'target','sc.bmp'); % same with default output filename
|
||
|
%
|
||
|
% Technical description:
|
||
|
% http://UndocumentedMatlab.com/blog/screencapture-utility/
|
||
|
%
|
||
|
% Bugs and suggestions:
|
||
|
% Please send to Yair Altman (altmany at gmail dot com)
|
||
|
%
|
||
|
% See also:
|
||
|
% imshow, imwrite, print
|
||
|
%
|
||
|
% Release history:
|
||
|
% 1.17 2016-05-16: Fix annoying warning about JavaFrame property becoming obsolete someday (yes, we know...)
|
||
|
% 1.16 2016-04-19: Fix for deployed application suggested by Dwight Bartholomew
|
||
|
% 1.10 2014-11-25: Added the 'print' target
|
||
|
% 1.9 2014-11-25: Fix for saving GIF files
|
||
|
% 1.8 2014-11-16: Fixes for R2014b
|
||
|
% 1.7 2014-04-28: Fixed bug when capturing interactive selection
|
||
|
% 1.6 2014-04-22: Only enable image formats when saving to an unspecified file via uiputfile
|
||
|
% 1.5 2013-04-18: Fixed bug in capture of non-square image; fixes for Win64
|
||
|
% 1.4 2013-01-27: Fixed capture of Desktop (root); enabled rbbox anywhere on desktop (not necesarily in a Matlab figure); enabled output to clipboard (based on Jiro Doke's imclipboard utility); edge-case fixes; added Java compatibility check
|
||
|
% 1.3 2012-07-23: Capture current object (uicontrol/axes/figure) if w=h=0 (e.g., by clicking a single point); extra input args sanity checks; fix for docked windows and image axes; include axes labels & ticks by default when capturing axes; use data-units position vector when capturing images; many edge-case fixes
|
||
|
% 1.2 2011-01-16: another performance boost (thanks to Jan Simon); some compatibility fixes for Matlab 6.5 (untested)
|
||
|
% 1.1 2009-06-03: Handle missing output format; performance boost (thanks to Urs); fix minor root-handle bug; added toolbar button option
|
||
|
% 1.0 2009-06-02: First version posted on <a href="http://www.mathworks.com/matlabcentral/fileexchange/authors/27420">MathWorks File Exchange</a>
|
||
|
|
||
|
% License to use and modify this code is granted freely to all interested, as long as the original author is
|
||
|
% referenced and attributed as such. The original author maintains the right to be solely associated with this work.
|
||
|
|
||
|
% Programmed and Copyright by Yair M. Altman: altmany(at)gmail.com
|
||
|
% $Revision: 1.17 $ $Date: 2016/05/16 17:59:36 $
|
||
|
|
||
|
% Ensure that java awt is enabled...
|
||
|
if ~usejava('awt')
|
||
|
error('YMA:screencapture:NeedAwt','ScreenCapture requires Java to run.');
|
||
|
end
|
||
|
|
||
|
% Ensure that our Java version supports the Robot class (requires JVM 1.3+)
|
||
|
try
|
||
|
robot = java.awt.Robot; %#ok<NASGU>
|
||
|
catch
|
||
|
uiwait(msgbox({['Your Matlab installation is so old that its Java engine (' version('-java') ...
|
||
|
') does not have a java.awt.Robot class. '], ' ', ...
|
||
|
'Without this class, taking a screen-capture is impossible.', ' ', ...
|
||
|
'So, either install JVM 1.3 or higher, or use a newer Matlab release.'}, ...
|
||
|
'ScreenCapture', 'warn'));
|
||
|
if nargout, imageData = []; end
|
||
|
return;
|
||
|
end
|
||
|
|
||
|
% Process optional arguments
|
||
|
paramsStruct = processArgs(varargin{:});
|
||
|
|
||
|
% If toolbar button requested, add it and exit
|
||
|
if ~isempty(paramsStruct.toolbar)
|
||
|
|
||
|
% Add the toolbar button
|
||
|
addToolbarButton(paramsStruct);
|
||
|
|
||
|
% Return the figure to its pre-undocked state (when relevant)
|
||
|
redockFigureIfRelevant(paramsStruct);
|
||
|
|
||
|
% Exit immediately (do NOT take a screen-capture)
|
||
|
if nargout, imageData = []; end
|
||
|
return;
|
||
|
end
|
||
|
|
||
|
% Convert position from handle-relative to desktop Java-based pixels
|
||
|
[paramsStruct, msgStr] = convertPos(paramsStruct);
|
||
|
|
||
|
% Capture the requested screen rectangle using java.awt.Robot
|
||
|
imgData = getScreenCaptureImageData(paramsStruct.position);
|
||
|
|
||
|
% Return the figure to its pre-undocked state (when relevant)
|
||
|
redockFigureIfRelevant(paramsStruct);
|
||
|
|
||
|
% Save image data in file or clipboard, if specified
|
||
|
if ~isempty(paramsStruct.target)
|
||
|
if strcmpi(paramsStruct.target,'clipboard')
|
||
|
if ~isempty(imgData)
|
||
|
imclipboard(imgData);
|
||
|
else
|
||
|
msgbox('No image area selected - not copying image to clipboard','ScreenCapture','warn');
|
||
|
end
|
||
|
elseif strncmpi(paramsStruct.target,'print',5) % 'print' or 'printer'
|
||
|
if ~isempty(imgData)
|
||
|
hNewFig = figure('visible','off');
|
||
|
imshow(imgData);
|
||
|
print(hNewFig);
|
||
|
delete(hNewFig);
|
||
|
else
|
||
|
msgbox('No image area selected - not printing screenshot','ScreenCapture','warn');
|
||
|
end
|
||
|
else % real filename
|
||
|
if ~isempty(imgData)
|
||
|
imwrite(imgData,paramsStruct.target);
|
||
|
else
|
||
|
msgbox(['No image area selected - not saving image file ' paramsStruct.target],'ScreenCapture','warn');
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
% Return image raster data to user, if requested
|
||
|
if nargout
|
||
|
imageData = imgData;
|
||
|
|
||
|
% If neither output formats was specified (neither target nor output data)
|
||
|
elseif isempty(paramsStruct.target) & ~isempty(imgData) %#ok ML6
|
||
|
% Ask the user to specify a file
|
||
|
%error('YMA:screencapture:noOutput','No output specified for ScreenCapture: specify the output filename and/or output data');
|
||
|
%format = '*.*';
|
||
|
formats = imformats;
|
||
|
for idx = 1 : numel(formats)
|
||
|
ext = sprintf('*.%s;',formats(idx).ext{:});
|
||
|
format(idx,1:2) = {ext(1:end-1), formats(idx).description}; %#ok<AGROW>
|
||
|
end
|
||
|
[filename,pathname] = uiputfile(format,'Save screen capture as');
|
||
|
if ~isequal(filename,0) & ~isequal(pathname,0) %#ok Matlab6 compatibility
|
||
|
try
|
||
|
filename = fullfile(pathname,filename);
|
||
|
imwrite(imgData,filename);
|
||
|
catch % possibly a GIF file that requires indexed colors
|
||
|
[imgData,map] = rgb2ind(imgData,256);
|
||
|
imwrite(imgData,map,filename);
|
||
|
end
|
||
|
else
|
||
|
% TODO - copy to clipboard
|
||
|
end
|
||
|
end
|
||
|
|
||
|
% Display msgStr, if relevant
|
||
|
if ~isempty(msgStr)
|
||
|
uiwait(msgbox(msgStr,'ScreenCapture'));
|
||
|
drawnow; pause(0.05); % time for the msgbox to disappear
|
||
|
end
|
||
|
|
||
|
return; % debug breakpoint
|
||
|
|
||
|
%% Process optional arguments
|
||
|
function paramsStruct = processArgs(varargin)
|
||
|
|
||
|
% Get the properties in either direct or P-V format
|
||
|
[regParams, pvPairs] = parseparams(varargin);
|
||
|
|
||
|
% Now process the optional P-V params
|
||
|
try
|
||
|
% Initialize
|
||
|
paramName = [];
|
||
|
paramsStruct = [];
|
||
|
paramsStruct.handle = [];
|
||
|
paramsStruct.position = [];
|
||
|
paramsStruct.target = '';
|
||
|
paramsStruct.toolbar = [];
|
||
|
paramsStruct.wasDocked = 0; % no false available in ML6
|
||
|
paramsStruct.wasInteractive = 0; % no false available in ML6
|
||
|
|
||
|
% Parse the regular (non-named) params in recption order
|
||
|
if ~isempty(regParams) & (isempty(regParams{1}) | ishandle(regParams{1}(1))) %#ok ML6
|
||
|
paramsStruct.handle = regParams{1};
|
||
|
regParams(1) = [];
|
||
|
end
|
||
|
if ~isempty(regParams) & isnumeric(regParams{1}) & (length(regParams{1}) == 4) %#ok ML6
|
||
|
paramsStruct.position = regParams{1};
|
||
|
regParams(1) = [];
|
||
|
end
|
||
|
if ~isempty(regParams) & ischar(regParams{1}) %#ok ML6
|
||
|
paramsStruct.target = regParams{1};
|
||
|
end
|
||
|
|
||
|
% Parse the optional param PV pairs
|
||
|
supportedArgs = {'handle','position','target','toolbar'};
|
||
|
while ~isempty(pvPairs)
|
||
|
|
||
|
% Disregard empty propNames (may be due to users mis-interpretting the syntax help)
|
||
|
while ~isempty(pvPairs) & isempty(pvPairs{1}) %#ok ML6
|
||
|
pvPairs(1) = [];
|
||
|
end
|
||
|
if isempty(pvPairs)
|
||
|
break;
|
||
|
end
|
||
|
|
||
|
% Ensure basic format is valid
|
||
|
paramName = '';
|
||
|
if ~ischar(pvPairs{1})
|
||
|
error('YMA:screencapture:invalidProperty','Invalid property passed to ScreenCapture');
|
||
|
elseif length(pvPairs) == 1
|
||
|
if isempty(paramsStruct.target)
|
||
|
paramsStruct.target = pvPairs{1};
|
||
|
break;
|
||
|
else
|
||
|
error('YMA:screencapture:noPropertyValue',['No value specified for property ''' pvPairs{1} '''']);
|
||
|
end
|
||
|
end
|
||
|
|
||
|
% Process parameter values
|
||
|
paramName = pvPairs{1};
|
||
|
if strcmpi(paramName,'filename') % backward compatibility
|
||
|
paramName = 'target';
|
||
|
end
|
||
|
paramValue = pvPairs{2};
|
||
|
pvPairs(1:2) = [];
|
||
|
idx = find(strncmpi(paramName,supportedArgs,length(paramName)));
|
||
|
if ~isempty(idx)
|
||
|
%paramsStruct.(lower(supportedArgs{idx(1)})) = paramValue; % incompatible with ML6
|
||
|
paramsStruct = setfield(paramsStruct, lower(supportedArgs{idx(1)}), paramValue); %#ok ML6
|
||
|
|
||
|
% If 'toolbar' param specified, then it cannot be left empty - use gcf
|
||
|
if strncmpi(paramName,'toolbar',length(paramName)) & isempty(paramsStruct.toolbar) %#ok ML6
|
||
|
paramsStruct.toolbar = getCurrentFig;
|
||
|
end
|
||
|
|
||
|
elseif isempty(paramsStruct.target)
|
||
|
paramsStruct.target = paramName;
|
||
|
pvPairs = {paramValue, pvPairs{:}}; %#ok (more readable this way, although a bit less efficient...)
|
||
|
|
||
|
else
|
||
|
supportedArgsStr = sprintf('''%s'',',supportedArgs{:});
|
||
|
error('YMA:screencapture:invalidProperty','%s \n%s', ...
|
||
|
'Invalid property passed to ScreenCapture', ...
|
||
|
['Supported property names are: ' supportedArgsStr(1:end-1)]);
|
||
|
end
|
||
|
end % loop pvPairs
|
||
|
|
||
|
catch
|
||
|
if ~isempty(paramName), paramName = [' ''' paramName '''']; end
|
||
|
error('YMA:screencapture:invalidProperty','Error setting ScreenCapture property %s:\n%s',paramName,lasterr); %#ok<LERR>
|
||
|
end
|
||
|
%end % processArgs
|
||
|
|
||
|
%% Convert position from handle-relative to desktop Java-based pixels
|
||
|
function [paramsStruct, msgStr] = convertPos(paramsStruct)
|
||
|
msgStr = '';
|
||
|
try
|
||
|
% Get the screen-size for later use
|
||
|
screenSize = get(0,'ScreenSize');
|
||
|
|
||
|
% Get the containing figure's handle
|
||
|
hParent = paramsStruct.handle;
|
||
|
if isempty(paramsStruct.handle)
|
||
|
paramsStruct.hFigure = getCurrentFig;
|
||
|
hParent = paramsStruct.hFigure;
|
||
|
else
|
||
|
paramsStruct.hFigure = ancestor(paramsStruct.handle,'figure');
|
||
|
end
|
||
|
|
||
|
% To get the acurate pixel position, the figure window must be undocked
|
||
|
try
|
||
|
if strcmpi(get(paramsStruct.hFigure,'WindowStyle'),'docked')
|
||
|
set(paramsStruct.hFigure,'WindowStyle','normal');
|
||
|
drawnow; pause(0.25);
|
||
|
paramsStruct.wasDocked = 1; % no true available in ML6
|
||
|
end
|
||
|
catch
|
||
|
% never mind - ignore...
|
||
|
end
|
||
|
|
||
|
% The figure (if specified) must be in focus
|
||
|
if ~isempty(paramsStruct.hFigure) & ishandle(paramsStruct.hFigure) %#ok ML6
|
||
|
isFigureValid = 1; % no true available in ML6
|
||
|
figure(paramsStruct.hFigure);
|
||
|
else
|
||
|
isFigureValid = 0; % no false available in ML6
|
||
|
end
|
||
|
|
||
|
% Flush all graphic events to ensure correct rendering
|
||
|
drawnow; pause(0.01);
|
||
|
|
||
|
% No handle specified
|
||
|
wasPositionGiven = 1; % no true available in ML6
|
||
|
if isempty(paramsStruct.handle)
|
||
|
|
||
|
% Set default handle, if not supplied
|
||
|
paramsStruct.handle = paramsStruct.hFigure;
|
||
|
|
||
|
% If position was not specified, get it interactively using RBBOX
|
||
|
if isempty(paramsStruct.position)
|
||
|
[paramsStruct.position, jFrameUsed, msgStr] = getInteractivePosition(paramsStruct.hFigure); %#ok<ASGLU> jFrameUsed is unused
|
||
|
paramsStruct.wasInteractive = 1; % no true available in ML6
|
||
|
wasPositionGiven = 0; % no false available in ML6
|
||
|
end
|
||
|
|
||
|
elseif ~ishandle(paramsStruct.handle)
|
||
|
% Handle was supplied - ensure it is a valid handle
|
||
|
error('YMA:screencapture:invalidHandle','Invalid handle passed to ScreenCapture');
|
||
|
|
||
|
elseif isempty(paramsStruct.position)
|
||
|
% Handle was supplied but position was not, so use the handle's position
|
||
|
paramsStruct.position = getPixelPos(paramsStruct.handle);
|
||
|
paramsStruct.position(1:2) = 0;
|
||
|
wasPositionGiven = 0; % no false available in ML6
|
||
|
|
||
|
elseif ~isnumeric(paramsStruct.position) | (length(paramsStruct.position) ~= 4) %#ok ML6
|
||
|
% Both handle & position were supplied - ensure a valid pixel position vector
|
||
|
error('YMA:screencapture:invalidPosition','Invalid position vector passed to ScreenCapture: \nMust be a [x,y,w,h] numeric pixel array');
|
||
|
end
|
||
|
|
||
|
% Capture current object (uicontrol/axes/figure) if w=h=0 (single-click in interactive mode)
|
||
|
if paramsStruct.position(3)<=0 | paramsStruct.position(4)<=0 %#ok ML6
|
||
|
%TODO - find a way to single-click another Matlab figure (the following does not work)
|
||
|
%paramsStruct.position = getPixelPos(ancestor(hittest,'figure'));
|
||
|
paramsStruct.position = getPixelPos(paramsStruct.handle);
|
||
|
paramsStruct.position(1:2) = 0;
|
||
|
paramsStruct.wasInteractive = 0; % no false available in ML6
|
||
|
wasPositionGiven = 0; % no false available in ML6
|
||
|
end
|
||
|
|
||
|
% First get the parent handle's desktop-based Matlab pixel position
|
||
|
parentPos = [0,0,0,0];
|
||
|
dX = 0;
|
||
|
dY = 0;
|
||
|
dW = 0;
|
||
|
dH = 0;
|
||
|
if ~isFigure(hParent)
|
||
|
% Get the reguested component's pixel position
|
||
|
parentPos = getPixelPos(hParent, 1); % no true available in ML6
|
||
|
|
||
|
% Axes position inaccuracy estimation
|
||
|
deltaX = 3;
|
||
|
deltaY = -1;
|
||
|
|
||
|
% Fix for images
|
||
|
if isImage(hParent) % | (isAxes(hParent) & strcmpi(get(hParent,'YDir'),'reverse')) %#ok ML6
|
||
|
|
||
|
% Compensate for resized image axes
|
||
|
hAxes = get(hParent,'Parent');
|
||
|
if all(get(hAxes,'DataAspectRatio')==1) % sanity check: this is the normal behavior
|
||
|
% Note 18/4/2013: the following fails for non-square images
|
||
|
%actualImgSize = min(parentPos(3:4));
|
||
|
%dX = (parentPos(3) - actualImgSize) / 2;
|
||
|
%dY = (parentPos(4) - actualImgSize) / 2;
|
||
|
%parentPos(3:4) = actualImgSize;
|
||
|
|
||
|
% The following should work for all types of images
|
||
|
actualImgSize = size(get(hParent,'CData'));
|
||
|
dX = (parentPos(3) - min(parentPos(3),actualImgSize(2))) / 2;
|
||
|
dY = (parentPos(4) - min(parentPos(4),actualImgSize(1))) / 2;
|
||
|
parentPos(3:4) = actualImgSize([2,1]);
|
||
|
%parentPos(3) = max(parentPos(3),actualImgSize(2));
|
||
|
%parentPos(4) = max(parentPos(4),actualImgSize(1));
|
||
|
end
|
||
|
|
||
|
% Fix user-specified img positions (but not auto-inferred ones)
|
||
|
if wasPositionGiven
|
||
|
|
||
|
% In images, use data units rather than pixel units
|
||
|
% Reverse the YDir
|
||
|
ymax = max(get(hParent,'YData'));
|
||
|
paramsStruct.position(2) = ymax - paramsStruct.position(2) - paramsStruct.position(4);
|
||
|
|
||
|
% Note: it would be best to use hgconvertunits, but:
|
||
|
% ^^^^ (1) it fails on Matlab 6, and (2) it doesn't accept Data units
|
||
|
%paramsStruct.position = hgconvertunits(hFig, paramsStruct.position, 'Data', 'pixel', hParent); % fails!
|
||
|
xLims = get(hParent,'XData');
|
||
|
yLims = get(hParent,'YData');
|
||
|
xPixelsPerData = parentPos(3) / (diff(xLims) + 1);
|
||
|
yPixelsPerData = parentPos(4) / (diff(yLims) + 1);
|
||
|
paramsStruct.position(1) = round((paramsStruct.position(1)-xLims(1)) * xPixelsPerData);
|
||
|
paramsStruct.position(2) = round((paramsStruct.position(2)-yLims(1)) * yPixelsPerData + 2*dY);
|
||
|
paramsStruct.position(3) = round( paramsStruct.position(3) * xPixelsPerData);
|
||
|
paramsStruct.position(4) = round( paramsStruct.position(4) * yPixelsPerData);
|
||
|
|
||
|
% Axes position inaccuracy estimation
|
||
|
if strcmpi(computer('arch'),'win64')
|
||
|
deltaX = 7;
|
||
|
deltaY = -7;
|
||
|
else
|
||
|
deltaX = 3;
|
||
|
deltaY = -3;
|
||
|
end
|
||
|
|
||
|
else % axes/image position was auto-infered (entire image)
|
||
|
% Axes position inaccuracy estimation
|
||
|
if strcmpi(computer('arch'),'win64')
|
||
|
deltaX = 6;
|
||
|
deltaY = -6;
|
||
|
else
|
||
|
deltaX = 2;
|
||
|
deltaY = -2;
|
||
|
end
|
||
|
dW = -2*dX;
|
||
|
dH = -2*dY;
|
||
|
end
|
||
|
end
|
||
|
|
||
|
%hFig = ancestor(hParent,'figure');
|
||
|
hParent = paramsStruct.hFigure;
|
||
|
|
||
|
elseif paramsStruct.wasInteractive % interactive figure rectangle
|
||
|
|
||
|
% Compensate for 1px rbbox inaccuracies
|
||
|
deltaX = 2;
|
||
|
deltaY = -2;
|
||
|
|
||
|
else % non-interactive figure
|
||
|
|
||
|
% Compensate 4px figure boundaries = difference betweeen OuterPosition and Position
|
||
|
deltaX = -1;
|
||
|
deltaY = 1;
|
||
|
end
|
||
|
%disp(paramsStruct.position) % for debugging
|
||
|
|
||
|
% Now get the pixel position relative to the monitor
|
||
|
figurePos = getPixelPos(hParent);
|
||
|
desktopPos = figurePos + parentPos;
|
||
|
|
||
|
% Now convert to Java-based pixels based on screen size
|
||
|
% Note: multiple monitors are automatically handled correctly, since all
|
||
|
% ^^^^ Java positions are relative to the main monitor's top-left corner
|
||
|
javaX = desktopPos(1) + paramsStruct.position(1) + deltaX + dX;
|
||
|
javaY = screenSize(4) - desktopPos(2) - paramsStruct.position(2) - paramsStruct.position(4) + deltaY + dY;
|
||
|
width = paramsStruct.position(3) + dW;
|
||
|
height = paramsStruct.position(4) + dH;
|
||
|
paramsStruct.position = round([javaX, javaY, width, height]);
|
||
|
%paramsStruct.position
|
||
|
|
||
|
% Ensure the figure is at the front so it can be screen-captured
|
||
|
if isFigureValid
|
||
|
figure(hParent);
|
||
|
drawnow;
|
||
|
pause(0.02);
|
||
|
end
|
||
|
catch
|
||
|
% Maybe root/desktop handle (root does not have a 'Position' prop so getPixelPos croaks
|
||
|
if isequal(double(hParent),0) % =root/desktop handle; handles case of hParent=[]
|
||
|
javaX = paramsStruct.position(1) - 1;
|
||
|
javaY = screenSize(4) - paramsStruct.position(2) - paramsStruct.position(4) - 1;
|
||
|
paramsStruct.position = [javaX, javaY, paramsStruct.position(3:4)];
|
||
|
end
|
||
|
end
|
||
|
%end % convertPos
|
||
|
|
||
|
%% Interactively get the requested capture rectangle
|
||
|
function [positionRect, jFrameUsed, msgStr] = getInteractivePosition(hFig)
|
||
|
msgStr = '';
|
||
|
try
|
||
|
% First try the invisible-figure approach, in order to
|
||
|
% enable rbbox outside any existing figure boundaries
|
||
|
f = figure('units','pixel','pos',[-100,-100,10,10],'HitTest','off');
|
||
|
drawnow; pause(0.01);
|
||
|
oldWarn = warning('off','MATLAB:HandleGraphics:ObsoletedProperty:JavaFrame');
|
||
|
jf = get(handle(f),'JavaFrame');
|
||
|
warning(oldWarn);
|
||
|
try
|
||
|
jWindow = jf.fFigureClient.getWindow;
|
||
|
catch
|
||
|
try
|
||
|
jWindow = jf.fHG1Client.getWindow;
|
||
|
catch
|
||
|
jWindow = jf.getFigurePanelContainer.getParent.getTopLevelAncestor;
|
||
|
end
|
||
|
end
|
||
|
com.sun.awt.AWTUtilities.setWindowOpacity(jWindow,0.05); %=nearly transparent (not fully so that mouse clicks are captured)
|
||
|
jWindow.setMaximized(1); % no true available in ML6
|
||
|
jFrameUsed = 1; % no true available in ML6
|
||
|
msg = {'Mouse-click and drag a bounding rectangle for screen-capture ' ...
|
||
|
... %'or single-click any Matlab figure to capture the entire figure.' ...
|
||
|
};
|
||
|
catch
|
||
|
% Something failed, so revert to a simple rbbox on a visible figure
|
||
|
try delete(f); drawnow; catch, end %Cleanup...
|
||
|
jFrameUsed = 0; % no false available in ML6
|
||
|
msg = {'Mouse-click within any Matlab figure and then', ...
|
||
|
'drag a bounding rectangle for screen-capture,', ...
|
||
|
'or single-click to capture the entire figure'};
|
||
|
end
|
||
|
uiwait(msgbox(msg,'ScreenCapture'));
|
||
|
|
||
|
k = waitforbuttonpress; %#ok k is unused
|
||
|
%hFig = getCurrentFig;
|
||
|
%p1 = get(hFig,'CurrentPoint');
|
||
|
positionRect = rbbox;
|
||
|
%p2 = get(hFig,'CurrentPoint');
|
||
|
|
||
|
if jFrameUsed
|
||
|
jFrameOrigin = getPixelPos(f);
|
||
|
delete(f); drawnow;
|
||
|
try
|
||
|
figOrigin = getPixelPos(hFig);
|
||
|
catch % empty/invalid hFig handle
|
||
|
figOrigin = [0,0,0,0];
|
||
|
end
|
||
|
else
|
||
|
if isempty(hFig)
|
||
|
jFrameOrigin = getPixelPos(gcf);
|
||
|
else
|
||
|
jFrameOrigin = [0,0,0,0];
|
||
|
end
|
||
|
figOrigin = [0,0,0,0];
|
||
|
end
|
||
|
positionRect(1:2) = positionRect(1:2) + jFrameOrigin(1:2) - figOrigin(1:2);
|
||
|
|
||
|
if prod(positionRect(3:4)) > 0
|
||
|
msgStr = sprintf('%dx%d area captured',positionRect(3),positionRect(4));
|
||
|
end
|
||
|
%end % getInteractivePosition
|
||
|
|
||
|
%% Get current figure (even if its handle is hidden)
|
||
|
function hFig = getCurrentFig
|
||
|
oldState = get(0,'showHiddenHandles');
|
||
|
set(0,'showHiddenHandles','on');
|
||
|
hFig = get(0,'CurrentFigure');
|
||
|
set(0,'showHiddenHandles',oldState);
|
||
|
%end % getCurrentFig
|
||
|
|
||
|
%% Get ancestor figure - used for old Matlab versions that don't have a built-in ancestor()
|
||
|
function hObj = ancestor(hObj,type)
|
||
|
if ~isempty(hObj) & ishandle(hObj) %#ok for Matlab 6 compatibility
|
||
|
try
|
||
|
hObj = get(hObj,'Ancestor');
|
||
|
catch
|
||
|
% never mind...
|
||
|
end
|
||
|
try
|
||
|
%if ~isa(handle(hObj),type) % this is best but always returns 0 in Matlab 6!
|
||
|
%if ~isprop(hObj,'type') | ~strcmpi(get(hObj,'type'),type) % no isprop() in ML6!
|
||
|
try
|
||
|
objType = get(hObj,'type');
|
||
|
catch
|
||
|
objType = '';
|
||
|
end
|
||
|
if ~strcmpi(objType,type)
|
||
|
try
|
||
|
parent = get(handle(hObj),'parent');
|
||
|
catch
|
||
|
parent = hObj.getParent; % some objs have no 'Parent' prop, just this method...
|
||
|
end
|
||
|
if ~isempty(parent) % empty parent means root ancestor, so exit
|
||
|
hObj = ancestor(parent,type);
|
||
|
end
|
||
|
end
|
||
|
catch
|
||
|
% never mind...
|
||
|
end
|
||
|
end
|
||
|
%end % ancestor
|
||
|
|
||
|
%% Get position of an HG object in specified units
|
||
|
function pos = getPos(hObj,field,units)
|
||
|
% Matlab 6 did not have hgconvertunits so use the old way...
|
||
|
oldUnits = get(hObj,'units');
|
||
|
if strcmpi(oldUnits,units) % don't modify units unless we must!
|
||
|
pos = get(hObj,field);
|
||
|
else
|
||
|
set(hObj,'units',units);
|
||
|
pos = get(hObj,field);
|
||
|
set(hObj,'units',oldUnits);
|
||
|
end
|
||
|
%end % getPos
|
||
|
|
||
|
%% Get pixel position of an HG object - for Matlab 6 compatibility
|
||
|
function pos = getPixelPos(hObj,varargin)
|
||
|
persistent originalObj
|
||
|
try
|
||
|
stk = dbstack;
|
||
|
if ~strcmp(stk(2).name,'getPixelPos')
|
||
|
originalObj = hObj;
|
||
|
end
|
||
|
|
||
|
if isFigure(hObj) %| isAxes(hObj)
|
||
|
%try
|
||
|
pos = getPos(hObj,'OuterPosition','pixels');
|
||
|
else %catch
|
||
|
% getpixelposition is unvectorized unfortunately!
|
||
|
pos = getpixelposition(hObj,varargin{:});
|
||
|
|
||
|
% add the axes labels/ticks if relevant (plus a tiny margin to fix 2px label/title inconsistencies)
|
||
|
if isAxes(hObj) & ~isImage(originalObj) %#ok ML6
|
||
|
tightInsets = getPos(hObj,'TightInset','pixel');
|
||
|
pos = pos + tightInsets.*[-1,-1,1,1] + [-1,1,1+tightInsets(1:2)];
|
||
|
end
|
||
|
end
|
||
|
catch
|
||
|
try
|
||
|
% Matlab 6 did not have getpixelposition nor hgconvertunits so use the old way...
|
||
|
pos = getPos(hObj,'Position','pixels');
|
||
|
catch
|
||
|
% Maybe the handle does not have a 'Position' prop (e.g., text/line/plot) - use its parent
|
||
|
pos = getPixelPos(get(hObj,'parent'),varargin{:});
|
||
|
end
|
||
|
end
|
||
|
|
||
|
% Handle the case of missing/invalid/empty HG handle
|
||
|
if isempty(pos)
|
||
|
pos = [0,0,0,0];
|
||
|
end
|
||
|
%end % getPixelPos
|
||
|
|
||
|
%% Adds a ScreenCapture toolbar button
|
||
|
function addToolbarButton(paramsStruct)
|
||
|
% Ensure we have a valid toolbar handle
|
||
|
hFig = ancestor(paramsStruct.toolbar,'figure');
|
||
|
if isempty(hFig)
|
||
|
error('YMA:screencapture:badToolbar','the ''Toolbar'' parameter must contain a valid GUI handle');
|
||
|
end
|
||
|
set(hFig,'ToolBar','figure');
|
||
|
hToolbar = findall(hFig,'type','uitoolbar');
|
||
|
if isempty(hToolbar)
|
||
|
error('YMA:screencapture:noToolbar','the ''Toolbar'' parameter must contain a figure handle possessing a valid toolbar');
|
||
|
end
|
||
|
hToolbar = hToolbar(1); % just in case there are several toolbars... - use only the first
|
||
|
|
||
|
% Prepare the camera icon
|
||
|
icon = ['3333333333333333'; ...
|
||
|
'3333333333333333'; ...
|
||
|
'3333300000333333'; ...
|
||
|
'3333065556033333'; ...
|
||
|
'3000000000000033'; ...
|
||
|
'3022222222222033'; ...
|
||
|
'3022220002222033'; ...
|
||
|
'3022203110222033'; ...
|
||
|
'3022201110222033'; ...
|
||
|
'3022204440222033'; ...
|
||
|
'3022220002222033'; ...
|
||
|
'3022222222222033'; ...
|
||
|
'3000000000000033'; ...
|
||
|
'3333333333333333'; ...
|
||
|
'3333333333333333'; ...
|
||
|
'3333333333333333'];
|
||
|
cm = [ 0 0 0; ... % black
|
||
|
0 0.60 1; ... % light blue
|
||
|
0.53 0.53 0.53; ... % light gray
|
||
|
NaN NaN NaN; ... % transparent
|
||
|
0 0.73 0; ... % light green
|
||
|
0.27 0.27 0.27; ... % gray
|
||
|
0.13 0.13 0.13]; % dark gray
|
||
|
cdata = ind2rgb(uint8(icon-'0'),cm);
|
||
|
|
||
|
% If the button does not already exit
|
||
|
hButton = findall(hToolbar,'Tag','ScreenCaptureButton');
|
||
|
tooltip = 'Screen capture';
|
||
|
if ~isempty(paramsStruct.target)
|
||
|
tooltip = [tooltip ' to ' paramsStruct.target];
|
||
|
end
|
||
|
if isempty(hButton)
|
||
|
% Add the button with the icon to the figure's toolbar
|
||
|
hButton = uipushtool(hToolbar, 'CData',cdata, 'Tag','ScreenCaptureButton', 'TooltipString',tooltip, 'ClickedCallback',['screencapture(''' paramsStruct.target ''')']); %#ok unused
|
||
|
else
|
||
|
% Otherwise, simply update the existing button
|
||
|
set(hButton, 'CData',cdata, 'Tag','ScreenCaptureButton', 'TooltipString',tooltip, 'ClickedCallback',['screencapture(''' paramsStruct.target ''')']);
|
||
|
end
|
||
|
%end % addToolbarButton
|
||
|
|
||
|
%% Java-get the actual screen-capture image data
|
||
|
function imgData = getScreenCaptureImageData(positionRect)
|
||
|
if isempty(positionRect) | all(positionRect==0) | positionRect(3)<=0 | positionRect(4)<=0 %#ok ML6
|
||
|
imgData = [];
|
||
|
else
|
||
|
% Use java.awt.Robot to take a screen-capture of the specified screen area
|
||
|
rect = java.awt.Rectangle(positionRect(1), positionRect(2), positionRect(3), positionRect(4));
|
||
|
robot = java.awt.Robot;
|
||
|
jImage = robot.createScreenCapture(rect);
|
||
|
|
||
|
% Convert the resulting Java image to a Matlab image
|
||
|
% Adapted for a much-improved performance from:
|
||
|
% http://www.mathworks.com/support/solutions/data/1-2WPAYR.html
|
||
|
h = jImage.getHeight;
|
||
|
w = jImage.getWidth;
|
||
|
%imgData = zeros([h,w,3],'uint8');
|
||
|
%pixelsData = uint8(jImage.getData.getPixels(0,0,w,h,[]));
|
||
|
%for i = 1 : h
|
||
|
% base = (i-1)*w*3+1;
|
||
|
% imgData(i,1:w,:) = deal(reshape(pixelsData(base:(base+3*w-1)),3,w)');
|
||
|
%end
|
||
|
|
||
|
% Performance further improved based on feedback from Urs Schwartz:
|
||
|
%pixelsData = reshape(typecast(jImage.getData.getDataStorage,'uint32'),w,h).';
|
||
|
%imgData(:,:,3) = bitshift(bitand(pixelsData,256^1-1),-8*0);
|
||
|
%imgData(:,:,2) = bitshift(bitand(pixelsData,256^2-1),-8*1);
|
||
|
%imgData(:,:,1) = bitshift(bitand(pixelsData,256^3-1),-8*2);
|
||
|
|
||
|
% Performance even further improved based on feedback from Jan Simon:
|
||
|
pixelsData = reshape(typecast(jImage.getData.getDataStorage, 'uint8'), 4, w, h);
|
||
|
imgData = cat(3, ...
|
||
|
transpose(reshape(pixelsData(3, :, :), w, h)), ...
|
||
|
transpose(reshape(pixelsData(2, :, :), w, h)), ...
|
||
|
transpose(reshape(pixelsData(1, :, :), w, h)));
|
||
|
end
|
||
|
%end % getInteractivePosition
|
||
|
|
||
|
%% Return the figure to its pre-undocked state (when relevant)
|
||
|
function redockFigureIfRelevant(paramsStruct)
|
||
|
if paramsStruct.wasDocked
|
||
|
try
|
||
|
set(paramsStruct.hFigure,'WindowStyle','docked');
|
||
|
%drawnow;
|
||
|
catch
|
||
|
% never mind - ignore...
|
||
|
end
|
||
|
end
|
||
|
%end % redockFigureIfRelevant
|
||
|
|
||
|
%% Copy screen-capture to the system clipboard
|
||
|
% Adapted from http://www.mathworks.com/matlabcentral/fileexchange/28708-imclipboard/content/imclipboard.m
|
||
|
function imclipboard(imgData)
|
||
|
% Import necessary Java classes
|
||
|
import java.awt.Toolkit.*
|
||
|
import java.awt.image.BufferedImage
|
||
|
import java.awt.datatransfer.DataFlavor
|
||
|
|
||
|
% Add the necessary Java class (ImageSelection) to the Java classpath
|
||
|
if ~exist('ImageSelection', 'class')
|
||
|
% Obtain the directory of the executable (or of the M-file if not deployed)
|
||
|
%javaaddpath(fileparts(which(mfilename)), '-end');
|
||
|
if isdeployed % Stand-alone mode.
|
||
|
[status, result] = system('path'); %#ok<ASGLU>
|
||
|
MatLabFilePath = char(regexpi(result, 'Path=(.*?);', 'tokens', 'once'));
|
||
|
else % MATLAB mode.
|
||
|
MatLabFilePath = fileparts(mfilename('fullpath'));
|
||
|
end
|
||
|
javaaddpath(MatLabFilePath, '-end');
|
||
|
end
|
||
|
|
||
|
% Get System Clipboard object (java.awt.Toolkit)
|
||
|
cb = getDefaultToolkit.getSystemClipboard; % can't use () in ML6!
|
||
|
|
||
|
% Get image size
|
||
|
ht = size(imgData, 1);
|
||
|
wd = size(imgData, 2);
|
||
|
|
||
|
% Convert to Blue-Green-Red format
|
||
|
imgData = imgData(:, :, [3 2 1]);
|
||
|
|
||
|
% Convert to 3xWxH format
|
||
|
imgData = permute(imgData, [3, 2, 1]);
|
||
|
|
||
|
% Append Alpha data (not used)
|
||
|
imgData = cat(1, imgData, 255*ones(1, wd, ht, 'uint8'));
|
||
|
|
||
|
% Create image buffer
|
||
|
imBuffer = BufferedImage(wd, ht, BufferedImage.TYPE_INT_RGB);
|
||
|
imBuffer.setRGB(0, 0, wd, ht, typecast(imgData(:), 'int32'), 0, wd);
|
||
|
|
||
|
% Create ImageSelection object
|
||
|
% % custom java class
|
||
|
imSelection = ImageSelection(imBuffer);
|
||
|
|
||
|
% Set clipboard content to the image
|
||
|
cb.setContents(imSelection, []);
|
||
|
%end %imclipboard
|
||
|
|
||
|
%% Is the provided handle a figure?
|
||
|
function flag = isFigure(hObj)
|
||
|
flag = isa(handle(hObj),'figure') | isa(hObj,'matlab.ui.Figure');
|
||
|
%end %isFigure
|
||
|
|
||
|
%% Is the provided handle an axes?
|
||
|
function flag = isAxes(hObj)
|
||
|
flag = isa(handle(hObj),'axes') | isa(hObj,'matlab.graphics.axis.Axes');
|
||
|
%end %isFigure
|
||
|
|
||
|
%% Is the provided handle an image?
|
||
|
function flag = isImage(hObj)
|
||
|
flag = isa(handle(hObj),'image') | isa(hObj,'matlab.graphics.primitive.Image');
|
||
|
%end %isFigure
|
||
|
|
||
|
%%%%%%%%%%%%%%%%%%%%%%%%%% TODO %%%%%%%%%%%%%%%%%%%%%%%%%
|
||
|
% find a way in interactive-mode to single-click another Matlab figure for screen-capture
|