% gr_raytrace('path') read the file as indicated by path (e.g.
% 'input.jpg') and view the original picture as well as the ray-traced
% version with a black hole in front of it.
%
% gr_raytrace('-path') same as above except rescale image to
% a smaller version (approximately 512x512 pixels).
%
function gr_raytrace(infile) %#codegen

%   Copyright 2010 The MathWorks, Inc.

% We apply assertions on the inputs (instead of specifying example arguments)
% As this program does not require dynamic memory allocation in general, we
% apply constraints on the size of the input string.
assert(isa(infile, 'char'));
assert(size(infile,1) == 1);
assert(size(infile,2) <= 1024);

coder.extrinsic('disp','figure','imread','image','set','gca','error','mat2cell');

% If the first character of the path is a '-' character, we interpret it as
% a rescaling operation. If there are two '-' characters, rescale to an 
% even smaller size.
rescaleImage = infile(1) == '-';
if rescaleImage
    infile = infile(2:end);
end
rescaleToReallySmall = infile(1) == '-';
if rescaleToReallySmall
    infile = infile(2:end);
end

% Put maximum bounds on the images, as we'd like to compile this program
% without the need for dynamic memory allocation.
coder.varsize('inImage', [2048 2048 3]);

% Initialize inImage before calling the extrinsic function. MATLAB Coder
% cannot analyze extrinsic functions, so we force the return type of the
% function using this trick.
inImage = zeros(0,0,3,'uint8');
inImage = imread(infile);

% Rescale image if necessary
if rescaleToReallySmall
    inImage = rescale(inImage, 64);
elseif rescaleImage
    inImage = rescale(inImage, 512);
end

% Setup figure windows
origFig = figure;
set(origFig, 'Position', [32 64 size(inImage, 2) size(inImage,1)]);
set(origFig, 'Name', 'Original Picture');
image(inImage);
set(gca, 'Visible', 'off');
set(gca, 'Position', [0 0 1 1]);

rayFig = figure;
origPos = [0 0 0 0];
origPos = get(origFig, 'Position');
xpos = origPos(1) + origPos(3) + 8;
ypos = origPos(2);
set(rayFig, 'Position', [xpos ypos size(inImage,2) size(inImage,1)]);
set(rayFig, 'Name', 'Ray-traced with Black Hole');
inImageHandle = image(inImage);
set(gca, 'Visible', 'off');
set(gca, 'Position', [0 0 1 1]);

% Apply raytracing on provided image
gr_raytrace_image(inImage, inImageHandle);

% This sub-function will be called periodically within the raytracing
% function. Each time we render what we've got so far.
function progress(imageHandle, imageData, y, height) %#ok
coder.extrinsic('set', 'drawnow');
coder.inline('never');
set(imageHandle, 'CData', imageData);
drawnow;

% Sub-function to shrink an image. We compute the average color value
% by looking at surrounding colors at each pixel. This gives a much
% better image quality (a technique known as anti-aliasing).
function im2 = shrinkImage(im, newW, newH)
% Never inline this function to improve code readability when doing code
% generation.
coder.inline('never');
assert(isa(im, 'uint8'));
assert(size(im,1) <= 2048);
assert(size(im,2) <= 2048);
assert(size(im,3) <= 3);
assert(newW <= 512);
assert(newH <= 512);
im2 = zeros(newH, newW, size(im,3), class(im));
h = size(im,1);
w = size(im,2);
for y = 1:newH
    y0 = int32(h*(y-1)/newH + 1);
    y1 = int32(h*(y)/newH + 1);
    if y1 > h, y1 = int32(h); end
    for x = 1:newW
        x0 = int32(w*(x-1)/newW + 1);
        x1 = int32(w*(x)/newW + 1);
        if x1 > w, x1 = int32(w); end
        cnt = 0;
        v = zeros(1,1,size(im,3),'int32');
        for i = y0:y1
            for j = x0:x1
                v = v + int32(im(i,j,:));
                cnt = cnt + 1;
            end
        end
        im2(y,x,:) = v/cnt;
    end
end

function inImage = rescale(inImage, newSize)
if size(inImage,1) > newSize || size(inImage,2) > newSize
    maxSz = max(size(inImage,1),size(inImage,2));
    newH = (size(inImage,1)/maxSz)*newSize;
    newW = (size(inImage,2)/maxSz)*newSize;
    inImage = shrinkImage(inImage, newW, newH);
end