Search code examples
matlabgraphicsvector-graphicsbitmapimage

Bitmap-render part of plot during vector-graphics export in MATLAB


I have a quite complex function that I plot using fsurf with a reasonably high MeshDensity (I cannot go much lower than the default, which is 35). Exporting this figure (saveas(gcf, 'file.pdf', 'pdf');) results in a 20-something MB pdf file of very nice quality, which nonetheless renders terribly slow. I would like to reduce the file size and, most importantly, the complexity of this pdf file, without exporting the whole plot (by which I mean the whole MATLAB figure) as a bitmap. How can I do that?

The perfect answer would explain how I can convert the surface plot (by which I mean, just the colored function surface on the white background) into a bitmap while maintaining the vectorized nature of axes and labels.

Update: Here's an example of such a plot.

enter image description here


Solution

  • This is my function BitmapRender, which Bitmap-renders part of the figure:

    %% Test Code
    clc;clf;
    Objects = surf(-4-2*peaks);
    hold('on');
    Objects(2 : 50) = plot(peaks);
    Objects(51) = imagesc([20 40], [0, 5], magic(100));
    hold('off');
    ylim([0 10]);
    zlim([-10 15]);
    Objects(1).Parent.GridLineStyle = 'none';
    view(45, 45);
    set(gcf, 'Color', 'white');
    rotate3d on
    
    saveas(gcf, 'pre.pdf');
    BitmapRender(gca, Objects(2 : 3 : end));
    % BitmapRender(gca, Objects(2 : 3 : end), [0.25 0.25 0.5 0.5], false);
    saveas(gcf, 'post.pdf');
    

    The function itself is pretty simple, except for the (re-)handling of visibility, as pressing the space key (after rotating, zooming etc) re-renders the figure.

    function BitmapRender(Axes, KeepObjects, RelativePosition, Draft, Key)
    
    if nargin < 2
        KeepObjects = [];
    end
    if nargin < 3
        RelativePosition = [0 0 1 1];
    end
    if nargin < 4
        Draft = false;
    end
    if nargin < 5
        Key = '';
    end
    
    Figure = Axes.Parent;
    FigureInnerWH = Figure.InnerPosition([3 4 3 4]);
    PixelPosition = round(RelativePosition .* FigureInnerWH);
    
    if isempty(Key)
        OverlayAxes = axes(Figure, 'Units', 'Normalized', 'Position', PixelPosition ./ FigureInnerWH);
        if Draft
            OverlayAxes.Box = 'on';
            OverlayAxes.Color = 'none';
            OverlayAxes.XTick = [];
            OverlayAxes.YTick = [];
            OverlayAxes.HitTest = 'off';
        else
            uistack(OverlayAxes, 'bottom');
            OverlayAxes.Visible = 'off';
        end
        setappdata(Figure, 'BitmapRenderOriginalVisibility', get(Axes.Children, 'Visible'));
    
        Axes.CLimMode = 'manual';
        Axes.XLimMode = 'manual';
        Axes.YLimMode = 'manual';
        Axes.ZLimMode = 'manual';
    
        hManager = uigetmodemanager(Figure);
        [hManager.WindowListenerHandles.Enabled] = deal(false);
        set(Figure, 'KeyPressFcn', @(f, e) BitmapRender(gca, KeepObjects, RelativePosition, Draft, e.Key));
    elseif strcmpi(Key, 'space')
        OverlayAxes = findobj(Figure, 'Tag', 'BitmapRenderOverlayAxes');
        delete(get(OverlayAxes, 'Children'));
        OriginalVisibility = getappdata(Figure, 'BitmapRenderOriginalVisibility');
        [Axes.Children.Visible] = deal(OriginalVisibility{:});
    else
        return;
    end
    
    if Draft
        return;
    end
    
    Axes.Visible = 'off';
    
    KeepObjectsVisibility = get(KeepObjects, 'Visible');
    [KeepObjects.Visible] = deal('off');
    
    drawnow;
    Frame = getframe(Figure, PixelPosition);
    
    [Axes.Children.Visible] = deal('off');
    Axes.Visible = 'on';
    Axes.Color = 'none';
    if numel(KeepObjects) == 1
        KeepObjects.Visible = KeepObjectsVisibility;
    else
        [KeepObjects.Visible] = deal(KeepObjectsVisibility{:});
    end
    
    Image = imagesc(OverlayAxes, Frame.cdata);
    uistack(Image, 'bottom');
    OverlayAxes.Tag = 'BitmapRenderOverlayAxes';
    OverlayAxes.Visible = 'off';
    
    end
    

    Obviously, the solution is pixel-perfect in terms of screen pixels. Two pdf files (pre and post) look like this. Note that surface, image and some plot lines are bitmap rendered, but some other plot lines, as well as axes and labels are still vectorized.

    enter image description here enter image description here