function [X,map] = readbmp(filename)
%READBMP Read image data from a BMP file.
%   [X,MAP] = READBMP(FILENAME) reads image data from a BMP file.
%   X is a uint8 array that is 2-D for 1-bit, 4-bit, and 8-bit
%   image data.  X is M-by-N-by-3 for 24-bit image data.  MAP is
%   normally an M-by-3 MATLAB colormap, but it may be empty if
%   the BMP file does not contain a colormap.
%
%   See also IMREAD, IMWRITE, IMFINFO, IMFORMATS.

%   Steven L. Eddins, June 1996
%   Copyright (c) 1984-98 by The MathWorks, Inc.
%   $Revision: 1.4 $  $Date: 1998/05/06 17:46:13 $

info = imbmpinfo(filename);

offset = info.ImageDataOffset;
width = info.Width;
height = info.Height;
filename = info.Filename;

map = info.Colormap;

switch info.CompressionType

case 'none'

    switch info.BitDepth
    case 1
        X = logical(bmpReadData1(filename, offset, width, height));
        
    case 4
        X = bmpReadData4(filename, offset, width, height);
        
    case 8
        X = bmpReadData8(filename, offset, width, height);

    case 24
        X = bmpReadData24(filename, offset, width, height);
        
    end
    
case '8-bit RLE'
    X = bmpReadData8RLE(filename, offset, width, height);
    
case '4-bit RLE'
    X = bmpReadData4RLE(filename, offset, width, height);
    
case 'bitfields'
    error('Bitfield compression not supported');
    
case 'Huffman 1D'
    error('Huffman 1D compression not supported');
    
case '24-bit RLE'
    error('24-bit RLE compression not supported');
    
end
        
%%%
%%% bmpReadData8 --- read 8-bit bitmap data
%%%
function X = bmpReadData8(fname, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 4*ceil(width/4);

X = freadu8(fname, offset, paddedWidth*height);
count = length(X);
if (count ~= paddedWidth*height)
    warning('Invalid BMP file: truncated image data');
    % Fill in the missing values with zeros.
    X(paddedWidth*height) = 0;
end

X = rot90(reshape(X, paddedWidth, height));
if (paddedWidth ~= width)
    X = X(:,1:width);
end



%%%
%%% bmpReadData8RLE --- read 8-bit RLE-compressed bitmap data
%%%
function X = bmpReadData8RLE(fname, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 4*ceil(width/4);

inBuffer = freadu8(fname, offset);
X = bmpdrle(inBuffer, paddedWidth, height, 'rle8');

X = rot90(X);
if (paddedWidth ~= width)
    X = X(:,1:width);
end



%%%
%%% bmpReadData4 --- read 4-bit bitmap data
%%%
function X = bmpReadData4(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 8*ceil(width/8);
numBytes = paddedWidth * height / 2; % evenly divides because of padding

XX = freadu8(filename, offset, numBytes);
count = length(XX);
if (count ~= numBytes)
    warning('Invalid BMP file: truncated image data');
    % Fill in the missing values with zeros.
    X(numBytes) = 0;
end
XX = reshape(XX, paddedWidth / 2, height);

X = repmat(uint8(0), paddedWidth, height);
X(1:2:end,:) = bitslice(XX,5,8);
X(2:2:end,:) = bitslice(XX,1,4);
X = rot90(X);
if (paddedWidth ~= width)
    X = X(:,1:width);
end


%%%
%%% bmpReadData4RLE --- read 4-bit RLE-compressed bitmap data
%%%
function X = bmpReadData4RLE(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 8*ceil(width/8);
numBytes = paddedWidth * height / 2; % evenly divides because of padding

X = rot90(bmpdrle(freadu8(filename, offset), paddedWidth, height, 'rle4'));
if (paddedWidth ~= width)
    X = X(:,1:width);
end


%%%
%%% bmpReadData1 --- read 1-bit bitmap data
%%%
function X = bmpReadData1(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
paddedWidth = 32*ceil(width/32);
numBytes = paddedWidth * height / 8;  % evenly divides because of padding

XX = freadu8(filename, offset, numBytes);
count = length(XX);
if (count ~= numBytes)
    warning('Invalid BMP file: truncated image data');
    % Fill in the missing values with zeros.
    XX(numBytes) = 0;
end
XX = reshape(XX, paddedWidth / 8, height);

X = repmat(uint8(0), paddedWidth, height);
X(1:8:end,:) = bitslice(XX,8,8);
X(2:8:end,:) = bitslice(XX,7,7);
X(3:8:end,:) = bitslice(XX,6,6);
X(4:8:end,:) = bitslice(XX,5,5);
X(5:8:end,:) = bitslice(XX,4,4);
X(6:8:end,:) = bitslice(XX,3,3);
X(7:8:end,:) = bitslice(XX,2,2);
X(8:8:end,:) = bitslice(XX,1,1);

X = rot90(X);
if (paddedWidth ~= width)
    X = X(:,1:width);
end


%%%
%%% bmpReadData24 --- read 24-bit bitmap data
%%%
function RGB = bmpReadData24(filename, offset, width, height)

% NOTE: BMP files are stored so that scanlines use a multiple of 4 bytes.
byteWidth = 3*width;
paddedByteWidth = 4*ceil(byteWidth/4);
numBytes = paddedByteWidth * height;

X = freadu8(filename, offset, numBytes);
count = length(X);
if (count ~= numBytes)
    warning('Invalid BMP file: truncated image data');
    % Fill in the missing values with zeros.
    X(numBytes) = 0;
end

X = rot90(reshape(X, paddedByteWidth, height));
if (paddedByteWidth ~= byteWidth)
    X = X(:,1:byteWidth);
end

RGB(1:height, 1:width, 3) = X(:,1:3:end);
RGB(:, :, 2) = X(:,2:3:end);
RGB(:, :, 1) = X(:,3:3:end);


