142 lines
3.7 KiB
Lua
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
|