if not modules then modules = { } end modules ['grph-bmp'] = {
    version   = 1.001,
    comment   = "companion to grph-inc.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local random = math.random
local context = context

local formatters, gsub, byte = string.formatters, string.gsub, string.byte
local setmetatableindex = table.setmetatableindex

local report_bitmap = logs.reporter("graphics","bitmap")

local bitmaps       = { }
graphics.bitmaps    = bitmaps

local wrapimage     = images.wrap

function bitmaps.new(xsize,ysize,colorspace,colordepth,mask,index)
    if not xsize or not ysize or xsize == 0 or ysize == 0 then
        report_bitmap("provide 'xsize' and 'ysize' larger than zero")
        return
    end
    if not colorspace then
        report_bitmap("provide 'colorspace' (1, 2, 3, 'gray', 'rgb', 'cmyk'")
        return
    end
    if not colordepth then
        report_bitmap("provide 'colordepth' (1, 2)")
        return
    end
    return graphics.identifiers.bitmap {
        colorspace = colorspace,
        colordepth = colordepth,
        xsize      = xsize,
        ysize      = ysize,
        mask       = mask and true or nil,
        index      = index and true or nil,
    }
end

local function flush(bitmap)
    local specification = backends.codeinjections.bitmap(bitmap)
    if specification then
        return wrapimage(specification)
    end
end

bitmaps.flush = flush

function bitmaps.tocontext(bitmap,width,height)
    local bmp = flush(bitmap)
    if bmp then
        if type(width) == "number" then
            width = width .. "sp"
        end
        if type(height) == "number" then
            height = height .. "sp"
        end
        if width or height then
            context.scale (
                {
                    width  = width,
                    height = height,
                },
                bmp
            )
        else
            context(bmp)
        end
    end
end

local function placeholder(nx,ny)

    local nx     = nx or 8
    local ny     = ny or nx
    local bitmap = bitmaps.new(nx,ny,"gray",1)
    local data   = bitmap.data

    for i=1,ny do
        local d = data[i]
        for j=1,nx do
            d[j] = random(100,199)
        end
    end

    return backends.codeinjections.bitmap(bitmap)

end

bitmaps.placeholder = placeholder


-- We can load a png or jpg file in a bytemap. For the png file we use a stripped
-- down loader and for jpg we just decode simple ones.

local   loaders = { }
bitmaps.loaders = loaders

do

    local newreader      = io.newreader
    local openstring     = utilities.streams.openstring
    local readstring     = utilities.streams.readstring
    local zlibdecompress = xzip.decompress
    local pngapplyfilter = pngdecode.applyfilter
    local pngsplitmask   = pngdecode.splitmask
    local loadbinfile    = resolvers.loadbinfile

    local function newcontent(filename)
        local found, data = loadbinfile(filename)
        return newreader(data or "", "string")
    end

    local function decodestrip(s,nx,ny,slice)
        local input = readstring(s,ny*(nx*slice+1))
        input = pngapplyfilter(input,nx,ny,slice)
        return input, false
    end

    local function decodemask(content,xsize,ysize,colordepth,colorspace)
        -- same as in lpdf-img.lmt
        local mask   = true
        local filter = false
        local bytes  = colordepth == 16 and 2 or 1
        local bpp    = colorspace == "DeviceRGB" and 3 or 1
        local slice  = bytes * (bpp + 1) -- always a mask
        content      = pngapplyfilter(content,xsize,ysize,slice)
        return pngsplitmask(content,xsize,ysize,bpp,bytes,mask,filter)
    end

    local function simplify(content,threshold)
        if not threshold then
            threshold = 127
        end
        local thresholds = setmetatableindex(function(t,k)
            local v = byte(k) < threshold and '0' or '1'
            t[k] = v
            return v
        end)
        return (gsub(content,".",thresholds))
    end

    loaders.png = function(filename,parameters)
        if not filename then
            return false
        end

        local threshold     = parameters.criterium
        local specification = figures.getinfo(filename)

        specification = specification and specification.status and specification.status.private

        if not specification then
            print("no specification")
            return
        end

        local colorspace  = specification.colorspace
        local xsize       = specification.xsize
        local ysize       = specification.ysize
        local zsize       = 1
        local colordepth  = specification.colordepth or 8
        local mask        = false

        if colordepth ~= 8 then
            print("unsupported color depth",colordepth)
            return
        end
        if colorspace == 0 then     -- gray
         -- zsize = 1
        elseif colorspace == 2 then -- rgb
            zsize = 3
        elseif colorspace == 4 then -- gray
            mask = true
        elseif colorspace == 6 then -- rgb
            zsize = 3
            mask  = true
        else
            print("unsupported color space",colorspace)
            return
        end
        if specification.interlace == 1 then
            print("unsupported interlace")
            return
        end
        local tables = specification.tables
        if not tables then
            print("no tables")
            return
        end
        local idat = tables.idat
        if not idat then
            print("no data")
            return
        end
        local pngfile = newcontent(filename,method,true)
        local content = idat[1]
        if type(content) == "table" then
            if not pngfile then
                return
            end
            content = idat(pngfile,true)
        end
        content = zlibdecompress(content)
if mask then
    content, mask = decodemask(content,xsize,ysize,colordepth,colorspace)
else
        content = decodestrip(openstring(content),xsize,ysize,zsize)
end
        if not parameters.raw and zsize == 1 then
            content = simplify(content,threshold)
        end
        pngfile:close()
        return xsize, ysize, zsize, content
    end

end

do

    loaders.jpg = function(filename,parameters)
        local j = io.loaddata(filename)
        if j then
            -- maybe also support simplify here
            return nanojpeg.decode(j)
        end
    end

    loaders.jpeg = loaders.jpg

end

function bitmaps.load(filename,parameters)
    local loader = loaders[file.suffix(filename)]
    if loader then
        return loader(filename,parameters or { })
    end
end
