function DriftingMaskedGratingTutorial(angle, cyclespersecond, f, drawmask)
% function DriftDemo2(angle, cyclespersecond, f, drawmask)
% ___________________________________________________________________
%
% Display an animated grating using the new Screen('DrawTexture') command.
% In the OS X Psychtoolbox Screen('DrawTexture') replaces
% Screen('CopyWindow'). The demo will stop after roughly 20 seconds have
% passed or after the user hits a key.
%
% This demo illustrates how to draw an animated grating online by use of
% only one grating texture. We create one texture with a static sine
% grating. In each successive frame we only draw a rectangular subregion of
% the sine-texture onto the screen, basically showing the texture through
% an aperture. The subregion - and therefore our "aperture" is shifted each
% frame, so we create the impression of a moving grating.
%
% The demo also shows how to use alpha-blending for masking the grating
% with a gaussian transparency mask (a texture with transparency layer).
%
% And finally, we demonstrate rotated drawing, as well as how to emulate
% the old OS-9 'WaitBlanking' command with the new 'Flip' command.
%
% Parameters:
%
% angle = Angle of the grating with respect to the vertical direction.
% cyclespersecond = Speed of grating in cycles per second.
% f = Frequency of grating in cycles per pixel.
% drawmask = If set to 1, then a gaussian aperture is drawn over the grating
%
% CopyWindow vs. DrawTexture:
%
% In the OS 9 Psychtoolbox, Screen ('CopyWindow") was used for all
% time-critical display of images, in particular for display of the movie
% frames in animated stimuli. In contrast, Screen('DrawTexture') should not
% be used for display of all graphic elements,  but only for  display of
% MATLAB matrices.  For all other graphical elements, such as lines,  rectangles,
% and ovals we recommend that these be drawn directly to the  display
% window during the animation rather than rendered to offscreen  windows
% prior to the animation.
%
% _________________________________________________________________________
% 
% see also: PsychDemos, MovieDemo

% HISTORY
%  6/7/05    mk     Adapted from Allen Ingling's DriftDemoOSX.m 

if nargin<4
    % By default, we mask the grating by a gaussian transparency mask:
    drawmask=1;
end

if nargin<3
    % Grating cycles/pixel
    f=0.05;
end

if nargin<2
    % Speed of grating in cycles per second:
    cyclespersecond=1;
end

if nargin<1
    % Angle of the grating: We default to 30 degrees.
    angle=30;
end

movieDurationSecs=20;   % Abort demo after 20 seconds.
texsize=100;            % Half-Size of the grating image.

% Screen('Preference', 'SkipSyncTests', 1);

try
	% This script calls Psychtoolbox commands available only in OpenGL-based 
	% versions of the Psychtoolbox. (So far, the OS X Psychtoolbox is the
	% only OpenGL-base Psychtoolbox.)  The Psychtoolbox command AssertPsychOpenGL will issue
	% an error message if someone tries to execute this script on a computer without
	% an OpenGL Psychtoolbox
	AssertOpenGL;
	
	% Get the list of screens and choose the one with the highest screen number.
	% Screen 0 is, by definition, the display with the menu bar. Often when 
	% two monitors are connected the one without the menu bar is used as 
	% the stimulus display.  Chosing the display with the highest dislay number is 
	% a best guess about where you want the stimulus displayed.  
	screens=Screen('Screens');
	screenNumber=max(screens);
	
    % Find the color values which correspond to white and black.  Though on OS
	% X we currently only support true color and thus, for scalar color
	% arguments,
	% black is always 0 and white 255, this rule is not true on other platforms will
	% not remain true on OS X after we add other color depth modes.  
	white=WhiteIndex(screenNumber);
	black=BlackIndex(screenNumber);
	gray=(white+black)/2;
	if round(gray)==white
		gray=black;
	end
	inc=white-gray;
	
	% Open a double buffered fullscreen window and draw a gray background 
	% to front and back buffers:
	[w screenRect]=Screen('OpenWindow',screenNumber, gray);

    % Enable alpha-blending:
    Screen('BlendFunction', w, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    % Show initial gray screen:
    Screen('Flip', w);

    % Calculate parameters of the grating:
    p=ceil(1/f);  % pixels/cycle    
    fr=f*2*pi;
    visiblesize=2*texsize+1;

    % Create one single static grating image:
    [x,y]=meshgrid(-2*texsize:2*texsize + p, -texsize:texsize);
    grating=gray + inc*cos(fr*x);

    % Store grating in texture:
    gratingtex=Screen('MakeTexture', w, grating);

    % Create a single gaussian transparency mask and store it to a texture:
    mask=ones(2*texsize+1, 2*texsize+1, 2) * gray;
    [x,y]=meshgrid(-1*texsize:1*texsize,-1*texsize:1*texsize);
    mask(:, :, 2)=white * (1 - exp(-((x/45).^2)-((y/45).^2)));
    masktex=Screen('MakeTexture', w, mask);
    
    % Query duration of monitor refresh interval:
    ifi=Screen('GetFlipInterval', w);
    
    waitframes = 1;
    waitduration = waitframes * ifi;
    
    % Translate requested speed of the grating (in cycles per second)
    % into a shift value in "pixels per frame", assuming given
    % waitduration: This is the amount of pixels to shift our "aperture" at
    % each redraw:
    shiftperframe= cyclespersecond * p * waitduration;
    
    Screen('TextSize', w, 24);
    
    % Slide 1: The static grating texture:
    [xp yp] = DrawFormattedText(w, 'Step 1: Generate a single static texture which shows the periodic grating pattern: The texture must contain at least two full periods of the pattern.\nPress a key to continue.\n\n', 0, 20, 0, 50);
    cmd = 'gratingtex = Screen(''MakeTexture'', w, gratingmatrix);\nScreen(''DrawTexture'', w, gratingtex);';
    [xp yp] = DrawFormattedText(w, cmd, xp, yp, 255);
    Screen('DrawTexture', w, gratingtex);
    Screen('Flip', w);

    GetClicks(w);
    
    % Slide 2: Cut out an aperture from the texture, and render it to a
    % dstRect of same size:

    autodrift = 0;
    xoffset = 0;
    angle = 0;
    i=0;

    while (1)
        [x y buttons] = GetMouse(w);

        if buttons(1) && ~autodrift
            xoffset = x - 100;
            xoffset = max(xoffset, 0);
            xoffset = min(xoffset, 2*texsize);
        end
              
        if buttons(2)
            autodrift = mod(autodrift+1, 3);
            while buttons(2)
                [x y buttons] = GetMouse(w);
            end
        end
        
        if autodrift >= 1
            % Shift the grating by "shiftperframe" pixels per frame:
            xoffset = mod(i*shiftperframe,p);
            i=i+1;
            
            if buttons(1)
                angle = x / RectWidth(screenRect) * 360.0;
            end
        end
                
        if autodrift == 0
            [xp yp] = DrawFormattedText(w, 'Step 2: Cut out a rectangular subregion ''srcRect'' from the texture and draw it to the screen area ''dstRect'' of same size. The texture is shown through an aperture.\nMoving the aperture creates apparent smooth motion. - Try it with the mouse!\nPress a key to continue.\n\n', 0, 20, 0, 50);
        else
            if autodrift >= 1
                [xp yp] = DrawFormattedText(w, 'Step 3: Horizontal shifting of the cut-out aperture region ''srcRect'' creates a nice drifting pattern. Subpixel accurate movements are possible due to hardware bilinear texture interpolation.\nPress a key to continue.\n\n', 0, 20, 0, 50);
                if angle~=0
                    [xp yp] = DrawFormattedText(w, '\nIt is also possible to rotate the ''dstRect'' destination region by some angle to create a rotated grating.\n', 0, yp, 0, 50);
                end
            end
            if autodrift == 2
                [xp yp] = DrawFormattedText(w, '\nOne can mask the ''dstRect'' destination region with a ''transparency mask'' texture by use of alpha-blending.\n', 0, yp, 0, 50);                
            end
        end
        
        cmd = 'Screen(''DrawTexture'', w, gratingtex, ';
        yp=yp+20;
        [xp] = DrawFormattedText(w, cmd, 0, yp, 255);
        cmd = 'srcRect, ';
        [xp] = DrawFormattedText(w, cmd, xp, yp, [255 0 0]);
        cmd = 'dstRect';
        [xp] = DrawFormattedText(w, cmd, xp, yp, [255 255 0]);
        if angle==0
            cmd = ');\n';
        else
            cmd = [', ' num2str(angle) ');\n'];
        end
        [xp yp] = DrawFormattedText(w, cmd, xp, yp, 255);
        
        % Draw static grating texture:
        dstRect = OffsetRect(Screen('Rect', gratingtex), 100, yp + 50);
        Screen('DrawTexture', w, gratingtex, [], dstRect);

        % Define shifted srcRect that cuts out the properly shifted rectangular
        % area from the texture:
        srcRect=[xoffset 0 xoffset + visiblesize visiblesize];
        apertRect = OffsetRect(srcRect, 100, yp + 50);
        Screen('FrameRect', w, [255 0 0], apertRect, 4);
        % Definition of the drawn rectangle on the screen:
        dstRect=[0 0 visiblesize visiblesize];
        dstRect=OffsetRect(dstRect, 600, yp + 150);
        Screen('DrawLine', w, [255 0 0 128], apertRect(1), apertRect(2), dstRect(1), dstRect(2), 4);
        Screen('DrawLine', w, [255 0 0 128], apertRect(3), apertRect(2), dstRect(3), dstRect(2), 4);
        Screen('DrawLine', w, [255 0 0 128], apertRect(1), apertRect(4), dstRect(1), dstRect(4), 4);
        Screen('DrawLine', w, [255 0 0 128], apertRect(3), apertRect(4), dstRect(3), dstRect(4), 4);
        % Draw grating texture:
        Screen('DrawTexture', w, gratingtex, srcRect, dstRect, angle);
        Screen('FrameRect', w, [255 255 0], dstRect, 4);
        
        if autodrift == 2
            maskRect = CenterRectOnPoint(dstRect, x, y);
            xp = 0;
            cmd = sprintf('Screen(''DrawTexture'', w, masktex, [], [%i %i %i %i], %f);\n', maskRect(1), maskRect(2), maskRect(3), maskRect(4), angle);
            [xp] = DrawFormattedText(w, cmd, xp, yp, 255);
            Screen('DrawTexture', w, masktex, [], maskRect, angle);
        end
        
        if KbCheck
            break;
        end
        
        Screen('Flip', w);
    end
        
    KbReleaseWait;
        
    % Perform initial Flip to sync us to the VBL and for getting an initial
    % VBL-Timestamp for our "WaitBlanking" emulation:
    vbl=Screen('Flip', w);

    i=0;
    dstRect = CenterRect(dstRect, screenRect);
    
    % Animationloop:
    while(1)
        % Shift the grating by "shiftperframe" pixels per frame:
        xoffset = mod(i*shiftperframe,p);
        i=i+1;
        
        % Define shifted srcRect that cuts out the properly shifted rectangular
        % area from the texture:
        srcRect=[xoffset 0 xoffset + visiblesize visiblesize];
        
        % Draw grating texture, rotated by "angle":
        Screen('DrawTexture', w, gratingtex, srcRect, dstRect, angle);

        if drawmask==1
            % Draw gaussian mask over grating: We need to subtract 0.5 from
            % the real size to avoid interpolation artifacts that are
            % created by the gfx-hardware due to internal numerical
            % roundoff errors when drawing rotated images:
            Screen('DrawTexture', w, masktex, [0 0 visiblesize visiblesize], dstRect, angle);
        end

        angle = angle + 0.1;
        
        % Flip 'waitframes' monitor refresh intervals after last redraw.
        vbl = Screen('Flip', w, vbl + (waitframes - 0.5) * ifi);

        % Abort demo if any key is pressed:
        if KbCheck
            break;
        end
    end

    %The same commands wich close onscreen and offscreen windows also close
	%textures.
	sca;

catch
    %this "catch" section executes in case of an error in the "try" section
    %above.  Importantly, it closes the onscreen window if its open.
    sca;
    psychrethrow(psychlasterror);
end %try..catch..
