%D \module
%D   [       file=meta-imp-bitmaps,
%D        version=2021.08.03,
%D          title=\METAPOST\ Graphics,
%D       subtitle=Bitmap Manipulations,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\registerctxluafile{libs-imp-imagemagick}{autosuffix}
\registerctxluafile{libs-imp-graphicsmagick}{autosuffix}

% todo: load png directly

\startluacode
    local char   = string.char
    local concat = table.concat
    local unpack = unpack

    graphics.bitmaps.magick = "imagemagick"

    function graphics.bitmaps.load(name)
        local inputname  = name
        local outputname = file.replacesuffix("m_k_i_v_mp_" .. name,"pnm")
        if file.suffix(inputname) == "pnm" then
            -- no need to convert
        else
            local whattodo   = table.setmetatableindex( {
                inputname  = inputname,
                outputname = outputname,
            }, specification)
            utilities[graphics.bitmaps.magick].convert(whattodo)
        end
        --
        local files = utilities.files
        local f = files.open(outputname)
        local function read(f)
            while true do
                local s = files.readcline(f)
                if not s then
                    return ""
                elseif string.find(s,"^#") then
                    -- again
                else
                    return s
                end
            end
        end
        local pnm   = read(f)
        local xy    = string.split(read(f)," ")
        local xsize = tonumber(xy[1])
        local ysize = tonumber(xy[2])
        local bsize = tonumber(read(f))
        local bitmap, colorspace
        if xsize and ysize and bsize then
            local read = files.readbytetable
            local data = { }
            if pnm == "P6" then
                colorspace = 2 -- rgb
                for i=1,ysize do
                    local tt = { }
                    for i=1,xsize do
                        tt[i] = read(f,3)
                    end
                    data[i] = tt
                end
            elseif pnm == "P5" then
                colorspace = 1 -- gray
                for i=1,ysize do
                    data[i] = read(f,xsize)
                end
            elseif pnm == "P4" then
                colorspace = 1
                -- todo
            end
            bitmap = {
                xsize      = xsize,
                ysize      = ysize,
                bsize      = bsize,
                inputname  = inputname,
                outputname = outputname,
                colorspace = colorspace,
                data       = data,
            }
        end
        files.close(f)
        return bitmap
    end

    function graphics.bitmaps.save(b)
        local xsize = b.xsize
        local ysize = b.ysize
        local bsize = b.bsize
        local colorspace = b.colorspace
        if xsize and ysize and bsize then
            local data = b.data
            local pnm  = nil
            if colorspace == 2 then
                pnm = "P6"
                for i=1,ysize do
                    local t = data[i]
                    for i=1,xsize do
                        t[i] = char(unpack(t[i]))
                    end
                    data[i] = concat(t)
                end
            elseif colorspace == 1 then
                pnm = "P5"
                if xsize > 1024 then
                    local t = data[i]
                    for i=1,xsize do
                        t[i] = char(t[i])
                    end
                else
                    for i=1,ysize do
                        data[i] = char(unpack(data[i]))
                    end
                end
            else
                return
            end
            local result = concat {
                pnm   .. "\r",
                xsize .. " " ..
                ysize .. "\r",
                bsize .. "\r",
                concat(data)
            }
            local outputname = file.replacesuffix(b.outputname,file.suffix(b.inputname))
            local inputname  = b.outputname
            io.savedata(inputname,result)
            --
            local whattodo = table.setmetatableindex( {
                inputname  = inputname,
                outputname = outputname,
            }, specification)
            utilities[graphics.bitmaps.magick].convert(whattodo)
            b.finalname = outputname
        end
    end

    graphics.bitmaps.manipulators = { }

    function graphics.bitmaps.manipulators.invert(b)
        local xsize = b.xsize
        local ysize = b.ysize
        local data  = b.data
        local colorspace = b.colorspace
        if colorspace == 1 then
            for r=1,ysize do
                local row = data[r]
                for c=1,xsize do
                    row[c] = 255 - row[c]
                end
            end
        elseif colorspace == 2 then
            for r=1,ysize do
                local row = data[r]
                for c=1,xsize do
                    local column = row[c]
                    column[1] = 255 - column[1]
                    column[2] = 255 - column[2]
                    column[4] = 255 - column[3]
                end
            end
        end
    end

\stopluacode

\continueifinputfile{meta-imp-bitmaps.mkxl}

\starttext

\startluacode

    local random = math.random

    function graphics.bitmaps.manipulators.myframe(b,f)
        local xsize = b.xsize
        local ysize = b.ysize
        local data  = b.data
        local colorspace = b.colorspace
        if colorspace == 1 then
            local f  = f or 20
            local fx = xsize - f + 1
            local fy = ysize - f + 1
            for c=1,xsize do
                for r=1,random(f-5,f+5) do
                    data[r][c] = random(0,255)
                end
                for r=ysize,random(fy-5,fy+5),-1 do
                    data[r][c] = random(0,255)
                end
            end
            for r=1,ysize do
                local row = data[r]
                for c=1,random(f-5,f+5) do
                    row[c] = random(0,255)
                end
                for c=xsize,random(fx-5,fx+5),-1 do
                    row[c] = random(0,255)
                end
            end
        elseif colorspace == 2 then
            local f  = f or 20
            local fx = xsize - f + 1
            local fy = ysize - f + 1
            for c=1,xsize do
                for r=1,random(f-5,f+5) do
                    local v = random(0,255)
                    data[r][c] = { v, v, v }
                end
                for r=ysize,random(fy-5,fy+5),-1 do
                    local v = random(0,255)
                    data[r][c] = { v, v, v }
                end
            end
            for r=1,ysize do
                local row = data[r]
                for c=1,random(f-5,f+5) do
                    local v = random(0,255)
                    row[c] = { v, v, v }
                end
                for c=xsize,random(fx-5,fx+5),-1 do
                    local v = random(0,255)
                    row[c] = { v, v, v }
                end
            end
        end
    end

    function graphics.bitmaps.manipulators.colorize(b,m)
        local xsize = b.xsize
        local ysize = b.ysize
        local data  = b.data
        local colorspace = b.colorspace
        if colorspace == 1 then
            for r=1,ysize do
                local row = data[r]
                for c=1,xsize do
                    local v = row[c]
                    row[c] = m[v] or { v, v, v }
                end
            end
            b.colorspace = 2
        end
    end

    function graphics.bitmaps.manipulators.evening(b,m)
        local xsize = b.xsize
        local ysize = b.ysize
        local data  = b.data
        local colorspace = b.colorspace
        if colorspace == 1 then
            for r=1,ysize do
                local row = data[r]
                for c=1,xsize do
                    row[c] = row[c] // 2
                end
            end
        end
    end

    context.startTEXpage()
        local b = graphics.bitmaps.load("hacker.jpg")
        graphics.bitmaps.manipulators.myframe(b,20)
        graphics.bitmaps.save(b)
        context.externalfigure { b.finalname }
    context.stopTEXpage()

    context.startTEXpage()
        local b = graphics.bitmaps.load("mill.png")
        graphics.bitmaps.manipulators.invert(b)
        graphics.bitmaps.manipulators.myframe(b,20)
        graphics.bitmaps.tocontext(b,50*65536,nil)
    context.stopTEXpage()

    do

        local m = table.setmetatableindex(function(t,k) local v = { k, k, k } t[k] = v return v end)

     -- local c = { 255,   0,   0 } for i=0x00,0x1F do m[i] = c end
     -- local c = {   0, 255,   0 } for i=0x20,0x3F do m[i] = c end
     -- local c = {   0,   0, 255 } for i=0x40,0x5F do m[i] = c end
     -- local c = { 255, 255,   0 } for i=0x60,0x7F do m[i] = c end
     -- local c = { 255,   0, 255 } for i=0x80,0x9F do m[i] = c end
     -- local c = {   0, 255, 255 } for i=0xA0,0xBF do m[i] = c end
     -- local c = { 255, 255, 255 } for i=0xC0,0xDF do m[i] = c end
     -- local c = {   0,   0,   0 } for i=0xE0,0xFF do m[i] = c end

        local c = {   0,   0, 255 } for i=0x40,0x5F do m[i] = c end
        local c = {   0, 255,   0 } for i=0x80,0x9F do m[i] = c end
        local c = { 255,   0,   0 } for i=0xB0,0xCF do m[i] = c end

        context.startTEXpage()
            local b = graphics.bitmaps.load("mill.png")
            graphics.bitmaps.manipulators.colorize(b,m)
            graphics.bitmaps.tocontext(b,50*65536,nil)
        context.stopTEXpage()

    end

    context.startTEXpage()
        local b = graphics.bitmaps.load("mill.png")
        graphics.bitmaps.manipulators.evening(b)
        graphics.bitmaps.tocontext(b,50*65536,nil)
    context.stopTEXpage()

\stopluacode

\protected\def\MyImage#1{\ctxlua{
    local b = graphics.bitmaps.load("#1")
    graphics.bitmaps.manipulators.invert(b)
    graphics.bitmaps.manipulators.myframe(b,20)
    graphics.bitmaps.tocontext(b,50*65536,nil)
}}

% This is inefficient because textext processes twice, so a "todo".

\startMPpage

    picture p ; p := textext("\MyImage{mill.png}") ;

    draw p ;
    draw (boundingbox p) enlarged -.5mm
        withpen pensquare scaled 1mm
        withcolor "darkred"
        withtransparency (1,.5)
    ;

\stopMPpage

\stoptext
