2018-12-08 18:04:35 +01:00

142 lines
3.7 KiB
Lua

-- ostro_zx.lua : converts a color image into a
-- ZX-like image (8+8 fixed colors with color clash)
-- using Ostromoukhov's error diffusion algorithm.
--
-- Version: 03/21/2017
--
-- Copyright 2016-2017 by Samuel Devulder
--
-- This program is free software; you can redistribute
-- it and/or modify it under the terms of the GNU
-- General Public License as published by the Free
-- Software Foundation; version 2 of the License.
-- See <http://www.gnu.org/licenses/>
run('../../thomson/lib/ostromoukhov.lua')
-- get screen size
local screen_w, screen_h = getpicturesize()
OtherDither = {}
function OtherDither:new(a)
local o = { -- default ZX values
-- width of the screen
width=a and a.width or 256,
-- height of the screen
height=a and a.height or 192,
-- size of Nx1 clash size
clash_size=a and a.clash_size or 8,
-- normalize the picture levels (like in imagemagick)
normalize=a and a.normalize or 0.005,
-- put a pixel
pset=a and a.pset or function(self,x,y,c)
if c<0 then c=-c-1 end
self.screen[x+y*self.width] = c
end,
-- init gfx data
setGfx=a and a.setGfx or function(self)
self.screen={}
end,
-- update gfx to screen
updatescreen=a and a.updatescreen or function(self)
for i=0,255 do setcolor(i,0,0,0) end
for y=0,self.height-1 do
for x=0,self.width-1 do
putpicturepixel(x,y,self.screen[x+y*self.width] or 0)
end
end
-- refresh palette
for i,v in ipairs(self.pal) do
local r=v % 16
local g=math.floor(v/16) % 16
local b=math.floor(v/256) % 16
setcolor(i+thomson._palette.offset-1,
thomson.levels.pc[r+1],
thomson.levels.pc[g+1],
thomson.levels.pc[b+1])
end
updatescreen()
end,
-- palette with thomson ordering (to use thomson's
-- lib support)
pal= a and a.pal or {
0x000,0xF00,0x00F,0xF0F,0x0F0,0xFF0,0x0FF,0xFFF,
0x000,0x200,0x002,0x202,0x020,0x220,0x022,0x222
}
}
setmetatable(o, self)
self.__index = self
return o
end
-- Converts ZX coordinates (0-255,0-191) into screen coordinates
function OtherDither:to_screen(x,y)
local i,j;
if screen_w/screen_h < self.width/self.height then
i = x*screen_h/self.height
j = y*screen_h/self.height
else
i = x*screen_w/self.width
j = y*screen_w/self.width
end
return math.floor(i), math.floor(j)
end
-- return the Color @(x,y) in linear space (0-255)
-- corresonding to the other platform screen
OtherDither._getLinearPixel = {} -- cache
function OtherDither:getLinearPixel(x,y)
local k=x+y*self.width
local p = self._getLinearPixel and self._getLinearPixel[k]
if not p then
local x1,y1 = self:to_screen(x,y)
local x2,y2 = self:to_screen(x+1,y+1)
if x2==x1 then x2=x1+1 end
if y2==y1 then y2=y1+1 end
p = Color:new(0,0,0)
for j=y1,y2-1 do
for i=x1,x2-1 do
p:add(getLinearPictureColor(i,j))
end
end
p:div((y2-y1)*(x2-x1)) --:floor()
if self._getLinearPixel then
self._getLinearPixel[k]=p
end
end
return self._getLinearPixel and p:clone() or p
end
function OtherDither:ccAcceptCouple(c1,c2)
-- bright colors can't mix with dimmed ones
return c1~=c2 and ((c1<=8 and c2<=8) or (c1>8 and c2>8))
end
function OtherDither:dither()
local NORMALIZE=Color.NORMALIZE
Color.NORMALIZE=self.normalize
local dither=OstroDither:new(self.pal)
dither.ccAcceptCouple = function(dither,c1,c2) return self:ccAcceptCouple(c1,c2) end
dither.clash_size = self.clash_size
dither.attenuation = .9
self:setGfx()
dither:ccDither(self.width,self.height,
function(x,y) return self:getLinearPixel(x,y) end,
function(x,y,c) self:pset(x,y,c) end,
true,
function(y)
thomson.info("Converting...",
math.floor(y*100/self.height),"%")
end,true)
-- refresh screen
setpicturesize(self.width,self.height)
self:updatescreen()
finalizepicture()
Color.NORMALIZE=NORMALIZE
end