class RGBQUAD
 
    export var rgbBlue, var rgbGreen, var rgbRed, var rgbReserved
 
    var rgbBlue : nat1
 
    var rgbGreen : nat1
 
    var rgbRed : nat1
 
    var rgbReserved : nat1
 
end RGBQUAD
 
 
class MONOCHROMESET
 
    export var color1, var color2, getColor, setBit, isSet
 
 
    var color1, color2 : int
 
    color1 := black
 
    color2 := white
 
    var bit : boolean := false
 
 
    fcn getColor : int
 
        if bit = true then
 
            result color1
 
        else
 
            result color2
 
        end if
 
    end getColor
 
 
    fcn isSet : boolean
 
        result bit
 
    end isSet
 
 
    proc setBit
 
        bit := true
 
    end setBit
 
end MONOCHROMESET
 
 
class Rectangle
 
    export x, y, w, h, setDimensions, setLocation
 
 
    var ix, iy : int
 
    var width, height : int
 
 
    proc setLocation (x, y : int)
 
        ix := x
 
        iy := y
 
    end setLocation
 
 
    proc setDimensions (w, h : int)
 
        width := w
 
        height := h
 
    end setDimensions
 
 
 
    fcn x : int
 
        result ix
 
    end x
 
    fcn y : int
 
        result iy
 
    end y
 
    fcn w : int
 
        result width
 
    end w
 
    fcn h : int
 
        result height
 
    end h
 
 
end Rectangle
 
 
 
class Image
 
    import Rectangle
 
    export Destruct, draw, getBounds, getInfoAt
 
 
    var bounds : ^Rectangle
 
    new bounds
 
    var file : string
 
    new bounds
 
 
    fcn getBounds : ^Rectangle
 
        result bounds
 
    end getBounds
 
 
    deferred proc draw
 
 
    deferred fcn getInfoAt (x, y : int) : ^anyclass
 
 
    proc Destruct
 
        free bounds
 
    end Destruct
 
end Image
 
 
class BitmapFile
 
    inherit Image
 
    import RGBQUAD, MONOCHROMESET
 
    export Construct, BITMAPFILEHEADER, BITMAPINFOHEADER,
 
        MONOCHROME, COLOR_16, COLOR_256, COLOR_16M, getFileHeader, setFileHeader,
 
        getInfoHeader, setInfoHeader
 
 
    % --constants--
 
    const MONOCHROME := 1
 
    const COLOR_16 := 4
 
    const COLOR_256 := 8
 
    const COLOR_16M := 24
 
 
    type BITMAPFILEHEADER :         % defaults               % location in file
 
        record
 
            bfType : nat2           %:= 19778                       % 1
 
            bfSize : nat4           %:= ??                          % 3
 
            bfReserved1 : nat2      %:= 0                           % 7
 
            bfReserved2 : nat2      %:= 0                           % 9
 
            bfOffBits : nat4        %:= 1078                        % 11
 
        end record
 
 
    type BITMAPINFOHEADER :
 
        record
 
            biSize : nat4           %:= 40                          % 15
 
            biWidth : int4          %:= 100                         % 19
 
            biHeight : int4         %:= 100                         % 23
 
            biPlanes : nat2         %:= 1                           % 27
 
            biBitCount : nat2       %:= COLOR_256                   % 29
 
            biCompression : nat4    %:= 0                           % 31
 
            biSizeImage : nat4      %:= 0                           % 35
 
            biXPelsPerMeter : int4  %:= 0                           % 39
 
            biYPelsPerMeter : int4  %:= 0                           % 43
 
            biClrUsed : nat4        %:= 0                           % 47
 
            biClrImportant : nat4   %:= 0                           % 51
 
        end record
 
 
    % --variables--
 
    var bitmapBits : flexible array 0 .. 0 of ^anyclass
 
    bounds -> setLocation (0, maxy)
 
    var fileName : string
 
    var byteWidth, padding : nat4
 
    var bitmapFileHeader : BITMAPFILEHEADER
 
    var bitmapInfoHeader : BITMAPINFOHEADER
 
 
    var bit : array 1 .. 8 of nat1
 
    bit (1) := 2#00000001
 
    bit (2) := 2#00000010
 
    bit (3) := 2#00000100
 
    bit (4) := 2#00001000
 
    bit (5) := 2#00010000
 
    bit (6) := 2#00100000
 
    bit (7) := 2#01000000
 
    bit (8) := 2#10000000
 
 
 
    fcn getFileHeader : BITMAPFILEHEADER
 
        result bitmapFileHeader
 
    end getFileHeader
 
 
    fcn getInfoHeader : BITMAPINFOHEADER
 
        result bitmapInfoHeader
 
    end getInfoHeader
 
 
    proc setFileHeader (p : BITMAPFILEHEADER)
 
        bitmapFileHeader := p
 
    end setFileHeader
 
 
    proc setInfoHeader (p : BITMAPINFOHEADER)
 
        bitmapInfoHeader := p
 
    end setInfoHeader
 
 
 
    proc _readBitmapFileHeader (sn : int)
 
        read : sn, bitmapFileHeader.bfType
 
        read : sn, bitmapFileHeader.bfSize
 
        read : sn, bitmapFileHeader.bfReserved1
 
        read : sn, bitmapFileHeader.bfReserved2
 
        read : sn, bitmapFileHeader.bfOffBits
 
    end _readBitmapFileHeader
 
 
 
    proc _readBitmapInfoHeader (sn : int)
 
        read : sn, bitmapInfoHeader.biSize
 
        read : sn, bitmapInfoHeader.biWidth
 
        read : sn, bitmapInfoHeader.biHeight
 
        read : sn, bitmapInfoHeader.biPlanes
 
        read : sn, bitmapInfoHeader.biBitCount
 
        read : sn, bitmapInfoHeader.biCompression
 
        read : sn, bitmapInfoHeader.biSizeImage
 
        read : sn, bitmapInfoHeader.biXPelsPerMeter
 
        read : sn, bitmapInfoHeader.biYPelsPerMeter
 
        read : sn, bitmapInfoHeader.biClrUsed
 
        read : sn, bitmapInfoHeader.biClrImportant
 
    end _readBitmapInfoHeader
 
 
 
    fcn _getPos (sn : int) : int
 
        var pos : int
 
        tell : sn, pos
 
        result pos
 
    end _getPos
 
 
 
    proc _read24BitData (sn : int)
 
        var r, g, b : nat1
 
        var rgbQuad : ^RGBQUAD
 
        var byteCount : nat4 := 0
 
 
        seek : sn, bitmapFileHeader.bfOffBits
 
        for i : 0 .. (bounds -> h * bounds -> w) - 1
 
            read : sn, b
 
            read : sn, g
 
            read : sn, r
 
            byteCount += 3
 
            new rgbQuad
 
            rgbQuad -> rgbBlue := b
 
            rgbQuad -> rgbGreen := g
 
            rgbQuad -> rgbRed := r
 
            bitmapBits (i) := rgbQuad
 
            if byteCount mod byteWidth = 0 then
 
                seek : sn, _getPos (sn) + padding
 
                byteCount := 0
 
            end if
 
        end for
 
    end _read24BitData
 
 
 
    proc _readMonochromeData (sn : int)
 
        var cbit8 : nat1 := 0
 
        var tmp : ^MONOCHROMESET := nil
 
        var byteCount, c : nat := 0
 
 
        seek : sn, bitmapFileHeader.bfOffBits
 
        for k : 0 .. (bounds -> h * bounds -> w) - 1 by 8
 
            read : sn, cbit8
 
            for decreasing i : 8 .. 1
 
                if c > bounds -> w * bounds -> h - 1 then
 
                    exit
 
                end if
 
                new tmp
 
                if (cbit8 and bit (i)) = bit (i) then
 
                    tmp -> setBit
 
                end if
 
                bitmapBits (c) := tmp
 
                c += 1
 
            end for
 
            byteCount += 1
 
            if byteCount mod byteWidth = 0 then
 
                byteCount := 0
 
                seek : sn, _getPos (sn) + padding
 
            end if
 
        end for
 
    end _readMonochromeData
 
 
 
    proc Construct (name : string)
 
        fileName := name
 
        var sn : int
 
        open : sn, fileName, read, seek
 
 
        seek : sn, 0
 
        _readBitmapFileHeader (sn)
 
        _readBitmapInfoHeader (sn)
 
        
 
        byteWidth := ceil (bitmapInfoHeader.biWidth * (bitmapInfoHeader.biBitCount / 8))
 
        padding := 4 - (byteWidth mod 4)
 
        if padding = 4 then
 
            padding := 0
 
        end if
 
 
        bounds -> setDimensions (bitmapInfoHeader.biWidth, bitmapInfoHeader.biHeight)
 
        new bitmapBits, (bounds -> h * bounds -> w) - 1
 
 
        case bitmapInfoHeader.biBitCount of
 
            label COLOR_16M :
 
                _read24BitData (sn)
 
            label MONOCHROME :
 
                _readMonochromeData (sn)
 
            label :
 
                assert false         % if u've reached here, it's probably cuz ur using a colour
 
                %                      resolution that is not implemented yet
 
        end case
 
        close : sn
 
    end Construct
 
 
 
    body fcn getInfoAt (x, y : int) : ^anyclass
 
        var tmp : ^anyclass := nil
 
        tmp := bitmapBits (((bounds -> h - y) * bounds -> w) - (bounds -> w - x))
 
        result tmp
 
    end getInfoAt
 
 
 
    body proc draw
 
        var tmp : ^anyclass
 
        var colr : int := RGB.AddColour (0, 0, 0)
 
        case bitmapInfoHeader.biBitCount of
 
            label COLOR_16M :
 
                for y : 0 .. bounds -> h - 1 by 1
 
                    for x : 0 .. bounds -> w - 1 by 1
 
                        tmp := getInfoAt (x, y)
 
                        RGB.SetColour (colr, RGBQUAD (tmp).rgbRed / 255, RGBQUAD (tmp).rgbGreen / 255, RGBQUAD (tmp).rgbBlue / 255)
 
                        drawdot (bounds -> x + x, bounds -> y - y, colr)
 
                    end for
 
                end for
 
            label MONOCHROME :
 
                for y : 0 .. bounds -> h - 1 by 1
 
                    for x : 0 .. bounds -> w - 1 by 1
 
                        tmp := getInfoAt (x, y)
 
                        colr := MONOCHROMESET (tmp).getColor
 
                        drawdot (bounds -> x + x, bounds -> y - y, colr)
 
                    end for
 
                end for
 
            label :
 
                assert false             % if u've reached here, it's probably cuz ur using a colour
 
                %                          resolution that is not implemented yet
 
        end case
 
    end draw
 
 
 
    body proc Destruct
 
        for i : 0 .. (bounds -> h * bounds -> w) - 1
 
            if bitmapBits (i) not= nil then
 
                free bitmapBits (i)
 
            end if
 
        end for
 
        free bitmapBits
 
        Image.Destruct
 
    end Destruct
 
end BitmapFile
 
 
  |