Advertisement
Guest User

MSX-GCR 1.0 by Leandro Correia 2019.

a guest
Apr 12th, 2019
377
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.10 KB | None | 0 0
  1. ; MSX-GCR 1.0 (MSX Graphic Conversion Routine) by Leandro Correia (2019). Many thanks to Rogerio Penchel, Rafael Jannone and Weber Kai for valuable information and support.
  2. ; Feel free to use this algorithm in any program you want (well, crediting me would be nice).
  3.  
  4. Graphics 512,250,32,3
  5.  
  6.  
  7. ; Parameters: ****************************************************************
  8.  
  9. ;Input and output images
  10.  
  11. ;Image to be loaded and converted. TIP: images are usually better converted to MSX1 if contrast is increased and a sharpen effect is used after reducing them (with bilinear interpolation).
  12. ; A good converter could allow to crop the image, resample it automatically and then allow user to increase contrast and sharpen it. ;)
  13.  
  14. imagem=LoadImage("imageinput.png")
  15. nomefinal$="imageoutput.bmp" ; Name of the output BMP
  16.  
  17. ;Color tolerance for dithering (from 0 to 100). Higher values mean dithering between colors that are not similar, which results in better color accuracy but ugly squares on degradees. 0 means no dithering
  18. tolerance=100
  19.  
  20. ;Name of the MSX screen file dump that'll be saved. This file can be loaded on a real MSX with a simple screen2:bload "name",s
  21. msxfilename$="image.scr"
  22.  
  23. ;Detail level (1 to 255);
  24. ;Lower values gives priority in areas with strong luminosity changes (usually good when tolerance value is high, or in photo conversions).
  25. ;Higher values makes the conversion ignore these luminosity changes (usually good when tolerance value is low or in simple art conversions).
  26. ;But you should test it. Usually 32.0 is a good value. 255 will make the routine completely ignore luminosity variations.
  27. ;At best, this value can give subtle (but still welcome) improvements in conversion quality.
  28.  
  29. detaillevel# = 32.0
  30.  
  31. ;******************************************************************************
  32.  
  33. DrawBlock imagem,0,0
  34.  
  35. Dim msxr(16),msxg(16),msxb(16)
  36. Dim octetr(8),octetg(8),octetb(8),octetdetail#(8)
  37. Dim octetfinal(8), octetvalue(8)
  38. Dim toner(5),toneg(5),toneb(5),distcolor#(5)
  39. Dim detail#(256,192) ; Detail map
  40. Dim imagedata(255,192) ; Luminosity data of original image
  41. Dim msxdumpdata(12288); Data for the MSX screen save file
  42.  
  43.  
  44. ;Reads all luminosity values
  45. For j=0To 191
  46. For i=0To 255
  47. GetColor(i,j)
  48. imagedata(i,j)=(ColorRed()+ColorGreen()+ColorBlue())/3
  49. Next
  50. Next
  51.  
  52. ;Calculates detail map
  53.  
  54. If detaillevel <255 Then
  55. For j=1To 191
  56. For i=1To 254
  57. cor=imagedata(i-1,j)
  58. cor2=imagedata(i,j)
  59. cor3=imagedata(i+1,j)
  60. dif1=Abs(cor-cor2)
  61. dif2=Abs(cor2-cor3)
  62. If dif1 > dif2 Then corfinal=dif1 Else corfinal=dif2
  63. cor=imagedata(i,j-1)
  64. cor3=imagedata(i,j+1)
  65. dif1=Abs(cor-cor2)
  66. dif2=Abs(cor2-cor3)
  67. If dif1 > dif2 Then corfinal2=dif1 Else corfinal2=dif2
  68. corfinal=(corfinal+corfinal2) Shr 1
  69. corfinal=corfinal
  70. detail(i,j)=corfinal
  71. Next
  72. Next
  73.  
  74. For i=0To 255
  75. detail(i,0)=0
  76. detail (i,191)=0
  77. Next
  78. For i=0To 191
  79. detail (0,i)=0
  80. detail (255,i)=0
  81. Next
  82.  
  83. For j=0To 191
  84. For i=0To 255
  85. If detail(i,j)<1 Then detail(i,j)=1
  86. detail(i,j)=(detail(i,j)/detaillevel#)+1
  87. Next
  88. Next
  89. Else
  90. For j=0To 191
  91. For i=0To 255
  92. detail(i,j)=1
  93. Next
  94. Next
  95. End If
  96.  
  97. WaitKey()
  98.  
  99.  
  100. ; Reads the MSX RGB color values at the "data" statement at the end of the program.
  101. For i=0To 15
  102. Read msxr(i),msxg(i),msxb(i)
  103. Next
  104.  
  105.  
  106. imgh=192:imgw=256
  107. y=0:x=0
  108. DrawBlock imagem,0,0
  109.  
  110.  
  111. While y<192
  112. bestdistance=99999999
  113. For i=0To 7
  114. ; Get the RGB values of 8 pixels of the original image
  115. GetColor x+i,y
  116. octetr(i)=ColorRed()
  117. octetg(i)=ColorGreen()
  118. octetb(i)=ColorBlue()
  119. octetdetail(i)=detail(x+i,y)
  120. Next
  121.  
  122. ; Brute force starts. Programs tests all 15 x 15 MSX color combinations. For each pixel octet it'll have to compare the original pixel colors with three different colors:
  123. ; two MSX colors and a mixed RGB of both. If this RGB mixed is chosen it'll later be substituted by dithering.
  124. For cor1=1To 15
  125. For cor2=cor1 To 15
  126.  
  127. dist=0
  128.  
  129. If KeyHit(1) Then End
  130.  
  131. ; First MSX color of the octet
  132. toner(0)=msxr(cor1)
  133. toneg(0)=msxg(cor1)
  134. toneb(0)=msxb(cor1)
  135.  
  136. ; Second MSX color of the octet
  137. toner(2)=msxr(cor2)
  138. toneg(2)=msxg(cor2)
  139. toneb(2)=msxb(cor2)
  140.  
  141.  
  142. ; A mix of both MSX colors RGB values. Since MSX cannot mix colors, later if this color is chosen it'll be substituted by a 2x2 dithering pattern.
  143. toner(1)=(msxr(cor1)+msxr(cor2))/2
  144. toneg(1)=(msxg(cor1)+msxg(cor2))/2
  145. toneb(1)=(msxb(cor1)+msxb(cor2))/2
  146.  
  147. If calcdist2000(msxr(cor1),msxg(cor1),msxb(cor1),msxr(cor2),msxg(cor2),msxb(cor2)) <= tolerance Then ; if colors are not too distant according to the tolerance parameter, octect will be dithered.
  148.  
  149. ; dithered
  150. For i=0To 7
  151. For j=0To 2
  152. distcolor(j)=(calcdist2000(toner(j),toneg(j),toneb(j),octetr(i),octetg(i),octetb(i)))*octetdetail(i)
  153. Next
  154. finaldist=distcolor(0):octetvalue(i)=0
  155. For j=1To 2
  156. If distcolor(j)<finaldist Then finaldist=distcolor(j):octetvalue(i)=j
  157. Next
  158.  
  159. dist=dist+finaldist
  160. If dist > bestdistance Then Exit
  161. Next
  162. Else
  163. ; not dithered
  164. For i=0To 7
  165. finaldista=(calcdist2000(toner(0),toneg(0),toneb(0),octetr(i),octetg(i),octetb(i)))*octetdetail(i)
  166. finaldistb=(calcdist2000(toner(2),toneg(2),toneb(2),octetr(i),octetg(i),octetb(i)))*octetdetail(i)
  167.  
  168. If finaldista < finaldistb Then
  169. octetvalue(i)=0
  170. finaldist=finaldista
  171. Else
  172. octetvalue(i)=2
  173. finaldist=finaldistb
  174. End If
  175. dist=dist+finaldist
  176. If dist > bestdistance Then Exit
  177. Next
  178. End If
  179.  
  180. If dist < bestdistance Then
  181. bestdistance=dist:bestcor1=cor1:bestcor2=cor2
  182. For i=0To 7
  183. octetfinal(i)=octetvalue(i)
  184. Next
  185. End If
  186. If bestdistance=0 Then Exit
  187. Next
  188. If bestdistance=0 Then Exit
  189. Next
  190. byte=0
  191. For i=0To 7
  192. Select octetfinal(i)
  193. Case 0
  194. Color msxr(bestcor1),msxg(bestcor1),msxb(bestcor1)
  195. Case 1
  196. If y Mod 2 = i Mod 2 Then Color msxr(bestcor2),msxg(bestcor2),msxb(bestcor2) Else Color msxr(bestcor1),msxg(bestcor1),msxb(bestcor1)
  197.  
  198. Case 2
  199. Color msxr(bestcor2),msxg(bestcor2),msxb(bestcor2)
  200. End Select
  201. If ColorRed()=msxr(bestcor2) And ColorGreen()=msxg(bestcor2) And ColorBlue()=msxb(bestcor2) Then byte=byte+2^(7-i)
  202. Plot 256+x+i,y
  203. Next
  204. y=y+1:If y Mod 8=0 Then y=y-8:x=x+8:If x>255 Then x=0:y=y+8
  205.  
  206. ; Bytes to be written in the final MSX screen dump file.
  207. msxdumpdata(bytepos)=byte
  208. msxdumpdata(bytepos+6144)=bestcor2*16+bestcor1
  209. bytepos=bytepos+1
  210. Wend
  211.  
  212. ; Saves screen in an MSX friendly format. To load it on a real MSX:
  213. ; 1 SCREEN2
  214. ; 2 BLOAD "FILENAME",S
  215.  
  216.  
  217. ; File header
  218. msxfile=WriteFile(msxfilename)
  219. WriteByte msxfile,$FE
  220. WriteByte msxfile,$00
  221. WriteByte msxfile,$00
  222. WriteByte msxfile,$ff
  223. WriteByte msxfile,$37
  224. WriteByte msxfile,$00
  225. WriteByte msxfile,$00
  226.  
  227. For i=0To 6143
  228. WriteByte msxfile,msxdumpdata(i)
  229. Next
  230.  
  231. For j=0To 2
  232. For i=0To 255
  233. WriteByte msxfile,i
  234. Next
  235. Next
  236.  
  237. For i=0To 1279
  238. WriteByte msxfile,0
  239. Next
  240.  
  241. For i=0To 6143
  242. WriteByte msxfile,msxdumpdata(i+6144)
  243. Next
  244. CloseFile msxfile
  245.  
  246. ; Using Blitz Basic routines to save a bitmap with the conversion.
  247.  
  248. final=CreateImage(256,192)
  249. CopyRect 256,0,256,192,0,0,FrontBuffer(),ImageBuffer(final)
  250. SaveBuffer ImageBuffer(final),nomefinal$
  251.  
  252. WaitKey() ; Waits for a keypress and ends the program.
  253. End
  254.  
  255.  
  256. Function calcdist2000#(r1#,g1#,b1#,r2#,g2#,b2#)
  257. If r1=r2 And g1 = g2 And b1 = b2 Then Return 3
  258.  
  259. ; Convert two RGB color values into Lab and uses the CIEDE2000 formula to calculate the distance between them.
  260. ; This function first converts RGBs to XYZ and then to Lab.
  261.  
  262. ; This is not optimized, but I did my best to make it readable. In some rare cases there are some weird colors, so MAYBE there's a small bug in the implementation.
  263. ; The RGB to Lab conversion in here could easily be substituted by a giant RGB to Lab lookup table, consuming much more memory, but gaining A LOT in speed.
  264.  
  265. ; Converting RGB values into XYZ
  266.  
  267. r#=r1#/255.0
  268. g#=g1#/255.0
  269. b#=b1#/255.0
  270.  
  271. If r# > 0.04045 Then r#=((r#+0.055)/1.055)^2.4 Else r#=r#/12.92
  272. If g# > 0.04045 Then g#=((g#+0.055)/1.055)^2.4 Else g#=g#/12.92
  273. If b# > 0.04045 Then b#=((b#+0.055)/1.055)^2.4 Else b#=b#/12.92
  274.  
  275. r#=r#*100.0
  276. g#=g#*100.0
  277. b#=b#*100.0
  278.  
  279. ;Observer. = 2°, Illuminant = D65
  280. x#=r#*0.4124 + g#*0.3576 + b#*0.1805
  281. y#=r#*0.2126 + g#*0.7152 + b#*0.0722
  282. z#=r#*0.0193 + g#*0.1192 + b#*0.9505
  283.  
  284. x#=x#/95.047 ;Observer= 2°, Illuminant= D65
  285. y#=y#/100.000
  286. z#=z#/108.883
  287.  
  288. If x# > 0.008856 Then x#=x# ^ (1.0/3.0) Else x# = (7.787 * x# ) + ( 16.0 / 116.0 )
  289. If y# > 0.008856 Then y#=y# ^ (1.0/3.0 ) Else y# = (7.787 * y# ) + ( 16.0 / 116.0 )
  290. If z# > 0.008856 Then z#=z# ^ (1.0/3.0 ) Else z# = (7.787 * z# ) + ( 16.0 / 116.0 )
  291.  
  292. l1# = ( 116.0 * y# ) - 16.0
  293. a1# = 500.0 * (x#-y#)
  294. b1# = 200.0 * (y#-z#)
  295.  
  296.  
  297. r#=r2#/255.0
  298. g#=g2#/255.0
  299. b#=b2#/255.0
  300.  
  301. If r# > 0.04045 Then r#=((r#+0.055)/1.055)^2.4 Else r#=r#/12.92
  302. If g# > 0.04045 Then g#=((g#+0.055)/1.055)^2.4 Else g#=g#/12.92
  303. If b# > 0.04045 Then b#=((b#+0.055)/1.055)^2.4 Else b#=b#/12.92
  304.  
  305. r#=r#*100.0
  306. g#=g#*100.0
  307. b#=b#*100.0
  308.  
  309. ;Observer. = 2°, Illuminant = D65
  310. x#=r#*0.4124 + g#*0.3576 + b#*0.1805
  311. y#=r#*0.2126 + g#*0.7152 + b#*0.0722
  312. z#=r#*0.0193 + g#*0.1192 + b#*0.9505
  313.  
  314.  
  315. x#=x#/95.047 ;Observer= 2°, Illuminant= D65
  316. y#=y#/100.000
  317. z#=z#/108.883
  318.  
  319. If x# > 0.008856 Then x#=x# ^ (1/3.0) Else x# = (7.787 * x# ) + ( 16.0 / 116.0 )
  320. If y# > 0.008856 Then y#=y# ^ (1/3.0 ) Else y# = (7.787 * y# ) + ( 16.0 / 116.0 )
  321. If z# > 0.008856 Then z#=z# ^ (1/3.0 ) Else z# = (7.787 * z# ) + ( 16.0 / 116.0 )
  322.  
  323. ; Converts XYZ to Lab...
  324.  
  325. l2# = (116.0 * y#) - 16.0
  326. a2# = 500.0 * (x#-y#)
  327. b2# = 200.0 * (y#-z#)
  328.  
  329. ; ...and then calculates distance between Lab colors, using the CIEDE2000 formula.
  330.  
  331. dl#=l2-l1
  332. hl#=l1+dl*0.5
  333. sqb1#=Float b1*b1
  334. sqb2#=Float b2*b2
  335. c1#=Sqr(Float a1*a1+sqb1)
  336. c2#=Sqr(Float a2*a2+sqb2)
  337. hc7#=Float ((c1+c2)*0.5)^Float 7
  338. trc#=Sqr(hc7/(hc7+6103515625.0))
  339. t2#=1.5-trc*0.5
  340. ap1#=a1*t2
  341. ap2#=a2*t2
  342. c1#=Sqr(ap1*ap1+sqb1)
  343. c2#=Sqr(ap2*ap2+sqb2)
  344. dc#=c2-c1
  345. hc#=c1+dc*0.5
  346. hc7#=hc^7.0
  347. trc#=Sqr(hc7/(hc7+6103515625.0))
  348. h1#=ATan2(b1,ap1)
  349. If h1<0 Then h1=h1+Pi*2.0
  350. h2#=ATan2(b2,ap2)
  351. If h2<0 Then h2=h2+Pi*2.0
  352. hdiff#=h2-h1
  353. hh#=h1+h2
  354. If Abs(hdiff)>Pi Then
  355. hh=hh+Pi*2
  356. If h2<=h1 Then hdiff=hdiff+Pi*2.0
  357. Else
  358. hdiff=hdiff-Pi*2.0
  359. End If
  360.  
  361. hh#=hh*0.5
  362. t2#=1-0.17*Cos(hh-Pi/6)+0.24*Cos(hh*2)
  363. t2#=t2+0.32*Cos(hh*3+Pi/30.0)
  364. t2#=t2-0.2*Cos(hh*4-Pi*63/180.0)
  365. dh#=2*Sqr(c1*c2)*Sin(hdiff*0.5)
  366. sqhl#=(hl-50.0)*(hl-50.0)
  367. fl#=dl/(1+(0.015*sqhl/Sqr(20.0+sqhl)))
  368. fc#=dc/(hc*0.045+1.0)
  369. fh=dh/(t2*hc*0.015+1.0)
  370. dt#=30*Exp(-(36.0*hh-55.0*Pi^2.0)/(25.0*Pi*Pi))
  371. r#=0-2*trc*Sin(2.0*dt*Pi/180.0)
  372. Return Sqr(fl*fl+fc*fc+fh*fh+r*fc*fh)
  373. End Function
  374.  
  375. ; Data of the MSX palette RGB values. Of course you could change these values to any palette you want.
  376. Data 0,0,0,0,0,0,36,219,36,109,255,109,36,36,255,73,109,255,182,36,36,73,219,255,255,36,36,255,109,109,219,219,36,219,219,146,36,146,36,219,73,182,182,182,182,255,255,255
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement