Разработка и ромхакинг > Инструменты
Софт для ромхакинга
Guyver(X.B.M.):
SeregaZ, у тебя изначально картинка кривая. Попробуй свой аватар этой прогой пропустить - и увидишь 15 цветов.
Sharpnull:
SeregaZ, я когда пытался конвертировать картинки в PNG для SGDK, понял что ImaGenesis - дикий мусор (сами видите), XnView и PIL (библиотека для Python) - дают не очень хороший результат, ImageMagick - кажется тоже не устроило, но я до конца не разобрался в настройках, Photoshop - хороший результат, узнать бы алгоритм, чтобы автоматически несколько картинок конвертировать.
Кстати, почему-то не нашёл программы для перестановки цветов в палитре в PNG, то есть чтобы менялся порядок, но цвета на изображении оставались на своём месте. В Photoshop'е с перестановкой цветов в палитре всё плохо, вручную если только. Смотрел в программах для пиксель арта, ничего такого нет, либо плохо смотрел.
Возможно гуру графики делают это как-то по-другому или потому что это самому пишется легко (в том же PIL). Вообще для Genesis есть настолько мощная графическая программа, что я с ходу не вкурил и забил.
--------
Забыл сказать, что хотя у Photoshop хороший результат, при импорте всё равно будет уменьшение диапазона цветов и некоторые могут совпадать, поэтому в идеале нужно самому выстраивать доступную палитру и перерисовывать.
SeregaZ:
Guyver(X.B.M.), да, с аватарой вышло лучше. порассматривал результаты: эта программа делает чуть светлее, моя соответственно темнее. плюс еще у себя в настройках функции нашел параметр Dither что-то такое. какая-то зернистость с ним добавляется. отключил во всех импортах изображений, кажется стало более плавнее.
Sharpnull, по моему у меня проблем, с изменением нумерации цветов возникнуть недолжно. другое дело, что у меня не графический редактор, а редактор для Дюны с элементами графического редактора... и изображение надо будет перевставить. то есть первая вставка создаст палитру этого изображения. потом меняем нужные цвета... эм... видимо вручную придется, перебивая значения RGB (точнее BGR), и после вставляя изображения со "вписыванием" в существующую палитру. то есть у меня два варианта импорта - со своей палитрой, и со "вписыванием" в существующую. тогда порядок в палитре будет изменен, изображение же сохранится. только номера цветов изменятся в самом изображении.
Добавлено позже:
и тем не менее вопрос с импортом своей палитры, с условием что два цвета из палитры должны быть не изменяемые - так и висит :) без него не получится правильно сделать импорт изображений для регионов. только если сохранять старую палитру и при вставке "вписывать" новое изображение в эту палитру.
Sharpnull:
SeregaZ, вот кстати, Photoshop справа (адаптивные 16 цветов, маджента сама встала первой) и слева как импорт сделал SGDK. Почти как ваш вариант, может деталей чуть больше.
--------
Обновил картинку, добавил оригинал справа для сравнения.
--------
--- Цитата: SeregaZ от 04 Сентября 2018, 20:24:57 ---нашел параметр Dither что-то такое. какая-то зернистость с ним добавляется.
--- Конец цитаты ---
У вас как раз было с Dither, картинка получается с ним более реалистичная, а без него получается обычная убогая постеризация:
Sharpnull:
--- Цитата: SeregaZ от 04 Сентября 2018, 16:43:04 ---есть чтоб на выходе было два постоянных цвета и только 7 те цвета,
--- Конец цитаты ---
В Photoshop и некоторых других программах можно выбрать произвольное количество цветов, вот результат с 7 цветами:
Также Photoshop адаптирует цвета картинки при вставке к палитре, если вставляете в индексируемую картинку (Image -> Mode -> Indexed Color), но можно просто экспортировть (File -> Export -> Save for Web) с 9 цветами (+1 для прозрачности, полагаю) и прям там изменить близкие цвета на те 2.
Или проблема в импорте именно в вашем редакторе Dune?
SeregaZ:
так ежели ужимать до 7 цветов - тот-же самый черный может потеряться, и стать скажем серым цветом. поэтому и хотелось бы подобрать какую-то dll или на худой конец консольную программу, которая могла бы подобное делать. типа назначаешь выходную палитру: 0 цвет фиолетовый, 8 цвет черный. с 1 по 7 программа может как угодно изменять. в моем же случае программа либо как хочет может изменять все цвета, либо ежели я резервирую тот нулевой цвет как фиолетовый - то программа по пастеризации его тупо не использует, создавая второй фиолетовый, по сути выкидывая один цвет. вот надо чтоб программа могла использовать уже заранее подготовленные 2 цвета, которые нельзя изменять, но можно использовать для выходной палитры и потом еще дописывать оставшиеся "динамические" цвета в эти слоты с 1 по 7. несколько сумбурно написал :) но это было бы наиболее верный подход к импорту изображения.
можно, конечно, выкрутится вручную подкрашивая элементы на изображении в существующие цвета. например трамп. фиолетовый фон будет прозрачным, но ведь надо его как-то обозначить. и это убьет один из цветов, если я буду импортировать в своем редакторе. получится трамп конвертится в 14, а не в 15 цветов. что можно сделать - взять этого самого трампа, пипеткой взять цвет из галстука - красный. и залить фиолетовый фон красным. после в редакторе залить этот красный на фоне фиолетовым-прозрачным. в итоге потерянный цвет спасен :) (картинки с отключенным Dither - выше он был включен, что придавало зернистости изображению.)
но это лишние телодвижения мышкой. хотелось бы чтоб программа сама это все делала. но код там замудреный, я смотрю и вижу фигу. что там происходит без малейшего понятия :) уж тем более теорию цветности я не знаю.
в принципе с черным можно сделать точно так-же, но опять таки это ручная работа, что убивает весь мой замысел. хочется однокликовости.
Ogr:
--- Цитата: SeregaZ от 04 Сентября 2018, 22:01:26 ---хочется однокликовости.
--- Конец цитаты ---
угу. а еще чтобы кто то твою задумку за тебя тебе реализовал. :lol:
SeregaZ:
ну... я не столько программист, сколько менеджер, собирающий в кучу то, что по плану должно быть в этом приложении :) и конца и края пока еще не видно. еще вагон и маленькую тележку ваять...
Guyver(X.B.M.):
Ну дык как кто-то должен понять, как работает конвертация в твоём редакторе? Хоть бы участки кода выложил, может, кто-то тебе и помог бы... :neznayu: Или ты там ничего не конвертируешь, а берёшь уже готовую картинку с нужным количеством цветов?
SeregaZ:
я беру готовый код, как бы модуль, запиленный юзерами с буржуйского форума для своего горе-языка. и с помощью него импортирую изображение в свой редактор.
сам код выкладывать как бы толку мало, ведь он не на популярных языках запилен :) и пока спал - думал. там среди функций есть некая POPULARITY_PALETTE, то есть согласно моему диванно-икспердному мнению там происходит подсчет количества пикселов одного цвета, и тем самым выставляется некий рейтинг цветов чтоль. какого цвета больше - того и тапки. тогда, опять таки согласно моей диванно-икспердной теории, если я заранее, каким-то образом, втолкаю в итоговый результат работы этой функции фиолетовый и черный с очень большим значением этого самого рейтинга, то эти цвета всегда будут на выходе, а сама функция будет подтягивать ближайшие цвета к этим двум, без их изменения.
--- Код: ---CompilerIf #PB_Compiler_IsMainFile
Enumeration
#Pal512
EndEnumeration
UsePNGImageDecoder()
UseOGGSoundDecoder()
;{ глобальная палитра
Global Dim FullPalette.l(513)
Dim col.a(8)
col(1) = 0
col(2) = 36
col(3) = 72
col(4) = 109
col(5) = 145
col(6) = 181
col(7) = 219
col(8) = 255
x = 0
y = 0
If CreateImage(#Pal512, 256, 32)
If StartDrawing(ImageOutput(#Pal512))
For i = 1 To 8
For l = 1 To 8
For k = 1 To 8
Box(x,y,4,4, RGB(col(k),col(l),col(i)))
num + 1
FullPalette(num) = RGB(col(k),col(l),col(i))
x + 4
Next
x - 32
y + 4
Next
x + 32
y - 32
Next
StopDrawing()
EndIf
EndIf ;}
; NearestColor module by Wilbert
; Latest updated : Jan 27, 2016
; Color distance formula based on:
; http://www.compuphase.com/cmetric.htm
; Dithering method:
; Sierra Lite
DeclareModule NearestColor
Prototype ProtoProgressCallback(PercentProgress.i)
Declare CatchPalette(*MemoryAddress.Long, NumColors.i)
Declare CopyPalette(Array DestinationArray.l(1))
Declare.i DitheredImage(Image.i, DitherLevel.a = 220, Brightness.b = 0, Contrast.b = 0, ProgressCallback.ProtoProgressCallback = 0)
Declare.l FindNearest(Color.l)
Declare.i PaletteColorCount()
Declare SetPalette(PaletteImage.i)
EndDeclareModule
Module NearestColor
EnableASM
;EnableExplicit
;DisableDebugger
Structure ColorScan
l.l[524288]
EndStructure
Global ColorScan.ColorScan
Global Dim IndexG.l(255)
Global Dim Palette.l(1)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
Macro rdx : edx : EndMacro
CompilerEndIf
Procedure.i PaletteColorCount()
; Return amount of colors the palette contains
ProcedureReturn ArraySize(Palette()) - 1
EndProcedure
Procedure CopyPalette(Array DestinationArray.l(1))
; Copy the current palette into a supplied array
Protected.i cnt = ArraySize(Palette()) - 1
ReDim DestinationArray(cnt - 1)
CopyMemory(@Palette(1), @DestinationArray(0), cnt << 2)
EndProcedure
Procedure CatchPalette(*MemoryAddress.Long, NumColors.i)
; Catch a palette from memory
Protected.i i, j = 1
ReDim Palette(NumColors + 1)
Palette(0) = 0 : Palette(NumColors + 1) = 0
For i = 1 To NumColors
Palette(i) = $ff000000 | *MemoryAddress\l
*MemoryAddress + 4
Next
SortStructuredArray(Palette(), 0, 0, #PB_Unicode, 1, NumColors)
For i = 0 To 255
IndexG(i) = j
While ((Palette(j) >> 8) & $ff) = i And j < NumColors
j + 1
Wend
IndexG(i) = (IndexG(i) + j) >> 1
Next
EndProcedure
Procedure SetPalette(PaletteImage.i)
; Set a palette from an image
Protected.i i, j, b, x, y, cnt
Protected.l c, c_
If StartDrawing(ImageOutput(PaletteImage))
FillMemory(@ColorScan, SizeOf(ColorScan))
j = OutputHeight() - 1
i = OutputWidth() - 1
; count all used colors
For y = 0 To j
For x = 0 To i
c = Point(x, y)
!mov eax, [p.v_c]
!rol ax, 8
!bswap eax
!shr eax, 8
lea rdx, [nearestcolor.v_ColorScan]
bts [rdx], eax
!jc nearestcolor.setpalette_cont0
inc cnt
!nearestcolor.setpalette_cont0:
Next
Next
StopDrawing()
; redim palette with room at top and bottom for zero entry
ReDim Palette(cnt + 1)
Palette(0) = 0 : Palette(cnt + 1) = 0
; set palette sorted on G, R, B and index on G
i = 0 : j = 1
For y = 0 To 255
IndexG(y) = j
For x = 0 To 2047
b = 0 : c_ = ColorScan\l[i]
While c_
shr c_, 1
!jnc nearestcolor.setpalette_cont1
!mov eax, [p.v_i]
!shl eax, 5
!or eax, [p.v_b]
!shl eax, 8
!or eax, 0xff
!bswap eax
!rol ax, 8
!mov [p.v_c], eax
Palette(j) = c : j + 1
!nearestcolor.setpalette_cont1:
b + 1
Wend
i + 1
Next
IndexG(y) = (IndexG(y) + j) >> 1
Next
EndIf
EndProcedure
Macro M_FindNearest(i, st)
!nearestcolor.findnearest#i#_loop:
!mov ecx, [p.v_c#i#]
!test ecx, ecx
!jz nearestcolor.findnearest#i#_cont2
!movzx eax, byte [p.v_Color + 1]
!movzx ecx, ch
!sub eax, ecx
!imul eax, eax
!shl eax, 11
!cmp eax, [p.v_bestd]
!jnc nearestcolor.findnearest#i#_cont1
!mov [p.v_d], eax
!movzx eax, byte [p.v_Color]
!movzx ecx, byte [p.v_c#i#]
!lea edx, [eax + ecx] ; edx = rsum
!sub eax, ecx
!imul eax, eax ; eax = r*r
!lea ecx, [edx + 0x400] ; ecx = $400 + rsum
!imul eax, ecx ; eax = ($400+rsum)*r*r
!add [p.v_d], eax
!movzx eax, byte [p.v_Color + 2]
!movzx ecx, byte [p.v_c#i# + 2]
!sub eax, ecx
!imul eax, eax ; eax = b*b
!neg edx
!add edx, 0x5fe ; edx = $5fe - rsum
!imul eax, edx ; eax = ($5fe-rsum)*b*b
!add eax, [p.v_d]
!cmp eax, [p.v_bestd]
!jnc nearestcolor.findnearest#i#_cont0
!mov [p.v_bestd], eax
!mov eax, [p.v_c#i#]
!mov [p.v_c], eax
!nearestcolor.findnearest#i#_cont0:
mov rdx, *p#i
add rdx, st
mov *p#i, rdx
mov eax, [rdx]
!mov [p.v_c#i#], eax
CompilerIf i = 1
!jmp nearestcolor.findnearest0_loop
CompilerElse
!jmp nearestcolor.findnearest1_loop
CompilerEndIf
!nearestcolor.findnearest#i#_cont1:
!mov dword [p.v_c#i#], 0
!nearestcolor.findnearest#i#_cont2:
CompilerIf i = 1
!cmp dword [p.v_c0], 0
!jnz nearestcolor.findnearest0_loop
CompilerEndIf
EndMacro
Procedure.l FindNearest(Color.l)
; Find the nearest color
Protected.l c, c0, c1, d, bestd = $12000000
Protected.Long *p0, *p1
!movzx eax, byte [p.v_Color + 1]
!mov [p.v_d], eax
*p1 = @Palette(IndexG(d)) : *p0 = *p1 - 4
c0 = *p0\l : c1 = *p1\l
M_FindNearest(0, -4)
M_FindNearest(1, 4)
ProcedureReturn c
EndProcedure
Macro M_DitherImage(offset, n = 1)
!movsx ecx, byte [p.v_err + offset]
!movsx eax, byte [p.v_err50 + offset]
!add ecx, eax
!imul ecx, edx
!sar ecx, 8
!movzx eax, byte [p.v_c0 + offset]
!add eax, [p.v_badd]
!imul eax, [p.v_cmul]
!sar eax, 8
!lea eax, [eax + ecx + 128]
!neg ah
!setz cl
!neg cl
!and al, cl
!sar ah, 7
!or al, ah
!mov [p.v_c0 + offset], al
EndMacro
Procedure.i DitheredImage(Image.i, DitherLevel.a = 220, Brightness.b = 0, Contrast.b = 0, ProgressCallback.ProtoProgressCallback = 0)
; Return a dithered image
; DitherLevel : 0 - 255
; Brightness : -128 - 127
; Contrast : -128 - 127
Protected.i result, x, y, w, h
Protected.l c0, c1, badd, cmul, err50, err
If ProgressCallback : ProgressCallback(0) : EndIf
result = CopyImage(Image, #PB_Any)
If result And StartDrawing(ImageOutput(result))
h = OutputHeight()
w = OutputWidth()
If DitherLevel = 0 And Brightness = 0 And Contrast = 0
While y < h
x = 0
While x < w
Plot(x, y, FindNearest(Point(x, y)))
x + 1
Wend
y + 1
If ProgressCallback
ProgressCallback(100 * y / h)
EndIf
Wend
Else
badd = Brightness - 128
cmul = (33280 * Contrast + 4259840) / (16640 - Contrast << 7)
Dim d_error.l(w)
While y < h
x = 0 : err50 = 0
While x < w
c0 = Point(x, y)
; add previous error
err = d_error(x)
!movzx edx, byte [p.v_DitherLevel]
M_DitherImage(0)
M_DitherImage(1)
M_DitherImage(2)
c1 = FindNearest(c0)
Plot(x, y, c1)
; calculate 50% error
!mov eax, [p.v_c0]
!mov ecx, [p.v_c1]
!mov edx, eax
!not edx
!and edx, ecx
!and edx, 0x01010101
!or eax, 0x01010101
!and ecx, 0xfefefefe
!sub eax, ecx
!xor eax, 0x01010101
!shr eax, 1
!sub eax, edx
!mov ecx, [p.v_err50]
!mov [p.v_err50], eax
; mix with previous error
!xor eax, 0x80808080
!xor ecx, 0x80808080
!mov edx, eax
!and edx, ecx
!and edx, 0x01010101
!and eax, 0xfefefefe
!and ecx, 0xfefefefe
!add eax, ecx
!shr eax, 1
!add eax, edx
!xor eax, 0x80808080
!mov [p.v_err], eax
d_error(x) = err
x + 1
Wend
d_error(0) << 1
y + 1
If ProgressCallback
ProgressCallback(100 * y / h)
EndIf
Wend
EndIf
StopDrawing()
EndIf
ProcedureReturn result
EndProcedure
DisableASM
DataSection
CGAPalette:
Data.l $000000, $AA0000, $00AA00, $AAAA00, $0000AA, $AA00AA, $0055AA, $AAAAAA
Data.l $555555, $FF5555, $55FF55, $FFFF55, $5555FF, $FF55FF, $55FFFF, $FFFFFF
EndDataSection
; Set default palette
CatchPalette(?CGAPalette, 16)
EndModule
CompilerEndIf
Structure PopType ; used in POPULARITY_PALETTE() procedure
clr.l ; color
pop.i ; popularity
EndStructure
Global Dim WK_Pal.l(0) ; <--- this array will contain the final palette
Procedure SCAN_FOR_PALETTE(image.i)
; obtain the palette of all colors used in an image
; stops if number of colors exceeds 512
Static NewMap Pmap.i(1024)
Static Dim Palette.l(0)
Protected c,i,x,y,Xmax,Ymax
Xmax = ImageWidth(image)-1
Ymax = ImageHeight(image)-1
StartDrawing(ImageOutput(image))
For y = 0 To Ymax
For x = 0 To Xmax
c = Point(x,y)
If MapSize(Pmap()) > 512
Break 2
EndIf
Pmap(Str(c)) = c
Next
Next
StopDrawing()
ReDim palette(MapSize(Pmap())-1)
i = 0
ForEach Pmap()
palette(i) = Pmap()
i + 1
Next
ClearMap(Pmap())
;Debug ArraySize(WK_Pal())
;For i = 0 To ArraySize(palette());ArraySize(WK_Pal())
; Debug Str(palette(i)); + " " + Str(WK_Pal(i))
;Next
;SHOW_PALETTE(Palette())
EndProcedure
Procedure ASSEMBLE_TO_PALETTE(Array palette.l(1), ImgRef.i, dither.i)
; assign each pixel of an image to the defined palette using NearestColor module
; ImgRef = the source image
; dither: 0 = no dither, 1 = dither
; A new image is created, the return value is the new image number.
NearestColor::CatchPalette(@palette(), ArraySize(palette())+1)
ProcedureReturn NearestColor::DitheredImage(ImgRef, dither*128)
EndProcedure
Procedure COUNT_COLORS(image.i)
; returns the number of unique colors in an image (24 bit)
Protected.i x, y, max_x, max_y, c, count, m
Dim m.a($1FFFFF)
StartDrawing(ImageOutput(image))
max_x = ImageWidth(image) - 1
max_y = ImageHeight(image) - 1
For y = 0 To max_y
For x = 0 To max_x
c = Point(x, y) & $FFFFFF
If m(c >> 3) & 1 << (c & 7) = 0
m(c >> 3) | 1 << (c & 7)
count + 1
EndIf
Next
Next
StopDrawing()
ProcedureReturn count
EndProcedure
Procedure POPULARITY_PALETTE(ImgRef.i,limit.i)
; Create a color palette with a modified popularity approach.
; ImgRef = the source image.
; Limit = the maximum number of colors in result.
; Limit count can be specified from 2 to 512. (512 is arbitrary limit)
; Required support procedures are:
; 'COUNT_COLORS', and 'ASSEMBLE_TO_PALETTE'.
; Finished palette is placed in the global array WK_Pal().
; This algorithm was created by BasicallyPure.
Static.i kb = $FF0000, kg = $00FF00, kr = $0000FF
Protected.i ImgWork, count, Xmin,Ymin,Xmax, Ymax, i, x, y, lum, d, br, da, mb, md
If IsImage(ImgRef)
ImgWork = CopyImage(ImgRef,#PB_Any)
If ImgWork
count = COUNT_COLORS(ImgWork)
If count <= limit : limit = count : EndIf
Else
ProcedureReturn 0
EndIf
Else
ProcedureReturn 0
EndIf
Xmax = ImageWidth(ImgWork) - 1
Ymax = ImageHeight(ImgWork) - 1
; 1) if color count is > 512 color count will be reduced.
; and palette colors may be altered.
If count > 512
; simple bitmask color reduction method
StartDrawing(ImageOutput(ImgWork))
For y = Ymin To yMax
For x = Xmin To xMax
Plot(x, y, Point(x,y) & $E0E0E0 | $0F0F0F)
Next x
Next y
StopDrawing()
EndIf
; 2) gather popularity data
NewMap Pmap.i()
StartDrawing(ImageOutput(ImgWork))
For y = Ymin To Ymax
For x = Xmin To Xmax
Pmap(Str(Point(x,y))) + 1
Next
Next
StopDrawing()
If MapSize(Pmap()) < limit : limit = MapSize(Pmap()) : EndIf
; 3) subdivide colors into 4 brightness lists
NewList bright.PopType()
NewList MedBri.PopType()
NewList MedDrk.PopType()
NewList dark.PopType()
ForEach Pmap()
d = Val(MapKey(Pmap()))
lum = (d & kr)<<1 + (d & kg) >> 6 + (d & kb) >> 16
If lum > 1338
AddElement(bright()) : bright()\clr = d : bright()\pop = Pmap()
ElseIf lum > 892
AddElement(MedBri()) : MedBri()\clr = d : MedBri()\pop = Pmap()
ElseIf lum > 446
AddElement(MedDrk()) : MedDrk()\clr = d : MedDrk()\pop = Pmap()
Else
AddElement(dark()) : dark()\clr = d : dark()\pop = Pmap()
EndIf
Next
; 4) sort each brightness lists by popularity
SortStructuredList(bright(),#PB_Sort_Descending,OffsetOf(PopType\pop),#PB_Integer)
SortStructuredList(MedBri(),#PB_Sort_Descending,OffsetOf(PopType\pop),#PB_Integer)
SortStructuredList(MedDrk(),#PB_Sort_Descending,OffsetOf(PopType\pop),#PB_Integer)
SortStructuredList(dark() ,#PB_Sort_Descending,OffsetOf(PopType\pop),#PB_Integer)
; 5) create the final palette
FirstElement(bright()) : br = ListSize(bright())
FirstElement(MedBri()) : mb = ListSize(MedBri())
FirstElement(MedDrk()) : md = ListSize(MedDrk())
FirstElement(dark()) : da = ListSize(dark())
limit - 1
ReDim WK_Pal(limit)
i = 0 : d = %00
Repeat ; pick from each list in turn the most popular color
If d = %00 And br > 0
WK_Pal(i) = bright()\clr
NextElement(bright())
i + 1 : br - 1
ElseIf d = %01 And da > 0
WK_Pal(i) = dark()\clr
NextElement(dark())
i + 1 : da - 1
ElseIf d = %10 And mb > 0
WK_Pal(i) = MedBri()\clr
NextElement(MedBri())
i + 1 : mb - 1
ElseIf d = %11 And md > 0
WK_Pal(i) = MedDrk()\clr
NextElement(MedDrk())
i + 1 : md - 1
EndIf
d = (d + %01) & %11
Until i > limit
If IsImage(ImgWork)
;CopyImage(ImgWork, ImgRef)
FreeImage(ImgWork)
EndIf
ProcedureReturn 1
EndProcedure
Procedure.a ImportImageWithPallete(ImgRef.l, limit, dither.i)
ret.a = 0
; repaint into 512 allow colors
NearestColor::CatchPalette(@FullPalette(), 513)
CBWidth = ImageWidth(ImgRef)
CBHeight = ImageHeight(ImgRef)
x = 0
y = 0
If StartDrawing(ImageOutput(ImgRef))
While y < CBHeight
x = 0
While x < CBWidth
Plot(x, y, NearestColor::FindNearest(Point(x, y)))
x + 1
Wend
y + 1
Wend
StopDrawing()
If POPULARITY_PALETTE(ImgRef, limit) ;generate the palette
Qimage = ASSEMBLE_TO_PALETTE(WK_Pal(), ImgRef, dither) ;build the final image
If IsImage(Qimage)
;ImageGadget(#PB_Any,0,0,w,h,ImageID(Qimage)) ;display the result
SCAN_FOR_PALETTE(Qimage) ; show the palette in a separate window
If IsImage(ImgRef)
FreeImage(ImgRef)
EndIf
CopyImage(Qimage, ImgRef)
FreeImage(Qimage)
ret = 1
EndIf
EndIf
EndIf
ProcedureReturn ret
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
GetClipboardImage(ImgRef, 32)
ImportImageWithPallete(ImgRef, 14, #True)
SaveImage(ImgRef, "D:\test.png")
;For i = 0 To ArraySize(WK_Pal())
; Debug WK_Pal(i)
;Next
CompilerEndIf
--- Конец кода ---
Guyver(X.B.M.):
Да, код просто монстрический. Я попробовал ради прикола один из алгоритмов реализовать, но у меня картинка сильно тёмная получается. И даже если осветлить её - цвета на оригинальные не всегда похожи... Хотя сам алгоритм похож на твой, с выборкой...
П.С. Опять нас ждёт хак "Трамп против @#тина"?
SeregaZ:
не :) просто более менее фотография Трампа попалась с нужным освещением, чтоб похожа, как освещение ментатов в дюне. а так у него глаза плохие - щелочки. анимацию рисовать для моргания глаз будет не удобно. оригинальные ментаты то с большими глазами, видно как зрачок влево-вправо двигается. а тут-же ничерта невидать :)
сам код из трех частей - моя криворукая часть, в том месте где я создаю палитру, как на приставке из 512 цветов.
после там код с асм вставками - это другого товарища, который растаскивает цвета на изображении к ближайшим разрешенным из 512.
третья часть уже сам код пастеризации второго товарища, с уменьшением количества цветов. вот видимо монструазность в виду подключения этого некоего фильтра или как он по научному - Dither. я думаю что с включенным этим Dither как-то квадратично. без него более плавно, а Sharpnull наоборот пишет что с Dither лучше.
по поводу яркости что-то я не знаю а будут ли соответствовать цвета, после такого изменения яркости тем, что разрешены? в смысле сеговские 512?
Guyver(X.B.M.):
Будут соответствовать, если изменение яркости происходит в пределах этих цветов разрешённых. У тебя в результате палитра получается отдельным файлом? В бмп 16 цветов же не включается палитра, да?
Имхо, Dither - это вообще шляпа. Никогда не использую в своих работах и стараюсь избавляться от этого шакального эффекта в изображениях...
SeregaZ:
палитру выковыривает из bmp изображения. а дальше можно поступить как угодно. в данном коде не сохраняет. там для себя выводил лист получившейся палитры:
--- Код: ---;For i = 0 To ArraySize(WK_Pal())
; Debug WK_Pal(i)
;Next
--- Конец кода ---
сохранение у меня в других местах :) не в этом коде. там где сохраняются регионы, где ментаты, где экраны и где большие иконки. там цвет надо перевернуть, так сказать, и потом сохранять. для переворачивания сделал что-то типа такого:
--- Код: ---Procedure.a Parse1Color(inp.a, pbtosmd.a=0)
out.a
If pbtosmd = 0
Select inp
Case 0
out = 0
Case 2
out = 36
Case 4
out = 72
Case 6
out = 109
Case 8
out = 145
Case $A
out = 181
Case $C
out = 219
Case $E
out = 255
EndSelect
Else
Select inp
Case 0
out = 0
Case 36
out = 2
Case 72
out = 4
Case 109
out = 6
Case 145
out = 8
Case 181
out = $A
Case 219
out = $C
Case 255
out = $E
EndSelect
EndIf
ProcedureReturn out
EndProcedure
Procedure.l Parse3Color(inp.u)
out.l
r = GetBits(inp, 0, 3) ;: Debug Hex(b)
g = GetBits(inp, 4, 7) ;: Debug Hex(g)
b = GetBits(inp, 8, 11) ;: Debug Hex(r)
out = RGB(Parse1Color(r), Parse1Color(g), Parse1Color(b))
ProcedureReturn out
EndProcedure
--- Конец кода ---
хотя это чтение. ну сохранение примерно так-же, то есть полученный RGB конвертируется в значение, как оно в файлах палитры идет двухбайтовое... эээ... BGR? хотя насчет таблицы значений я в последнее время не уверен. цвета то похожи, но где-то в соседней теме видел чуть чуть другие значения, то есть числа рядышком, но все-таки другие. ну мне колхознику пойдет :)
Добавлено позже:
и у меня вроде что-то получается :) насильно засунул фиолетовый в просчитываемые цвета и в результате он всегда есть в выходной палитре, независимо от того, есть ли он на изображении или нет. теперь надо сделать вилку, даже тройник. три варианта развития событий - как есть, учитывать фиолетовый есть или нету на изображении, учитывать черный есть или нету на изображении. и то, причем не фиолетовый, а любой какой укажешь. доволен как слон :) НО! спасти красный цвет так и не получилось. слишком мало места красным цветом на Трампе покрашено и в результате у красного маленький рейтинг. не смотря что он ярче, так сказать, в палитру в итоге вошел более блеклый какой-то цвет, просто у него был лучше рейтинг.
Sharpnull:
--- Цитата: Guyver(X.B.M.) от 05 Сентября 2018, 14:46:00 ---Имхо, Dither - это вообще шляпа. Никогда не использую в своих работах и стараюсь избавляться от этого шакального эффекта в изображениях...
--- Конец цитаты ---
Не знал, что такие люди бывают. Как же вы играете в игры для ретроконсолей, там же повсеместно используется Dither, невозможно ограничить цвета и чтобы выглядело хорошо без этого (для объёмных изображений, пихать везде очевидно нет смысла).
SeregaZ, я бы на вашем месте не возился из-за одного цвета, а почему с маджентой проблемы не понял, в хорошем алгоритме можно задать что считать прозрачностью и он не должен считать его цветом, а просто отметить отдельным индексом. Если вы управляете "популярностью", то можно занижать мадженту (цвет прозрачности) до минимуму, чтобы никогда не попадал в палитру (так в конвертерах и происходит), и после обработки тупо в те пиксели вставить индекс прозрачного цвета. (UPD: Глупость сморозил, можно же просто игнорить пиксели с прозрачностью.) Хотя наверно я неправильно понял и вам именно маджента и нужна в итоге...
Галстук никогда не будет таким, какой вы хотите. Выход из конвертера нельзя назвать пиксель артом, потому что пиксель арт - это когда художник ставит в определённое место определённый цвет. Я к тому, что красиво автоматом не получится.
--------
И опять же пастеризуют молоко, а картинки постеризуют.
SeregaZ:
ну вот... неожиданно развязал холивар :)
этот код не различает прозрачный или нет. ему подавай количество выходных цветов и все на этом. поэтому я и делал не в 16, а в 15 цветов постеризацию (гугель хрому не нравится через о), всегда резервируя 0 цвет как фиолетовый. однако если в изображении был фиолетовый - в итоговой палитре оказывалось два фиолетовых. то есть 1 цвет терялся. теперь же, добавив обязательное наличие в выходной палитре фиолетового, я могу делать в 16 цветов. просто в конце нужна будет сортировка палитры, чтоб сдвигать фиолетовый (либо какой другой цвет) на 0 номер. этот же трюк буду проворачивать и с регионами - добавляя обязательно черный, независимо от его наличия на изображении. он должен быть под 15 номером. 0 фиолетовый, 1-7 постеризация может изменять, 8-14 опять фиолетовый, а вобще пустые не используемые и 15 черный.
SeregaZ:
добавил в опциях галку для этого самого Dither и во всех четырех местах, где у меня импорт добавил эту переменную.
--- Код: ---CheckBoxGadget(#OptionsDither, x, y, 915, 20, "Use Dither for import images | Использовать Dither для импорта изображений")
--- Конец кода ---
ну и... пофиксил глаза для Трампа :)))))
morskoyzmey:
Не знаю, актуален ли заголовок топика, но внесу свои 5 копеек.
HxD
Удобный хекс редактор. Есть кодировки, сравнение файлов, чек сумма, настраиваемая тема.
Русский язык можно найти в гугле.
https://mh-nexus.de/en/hxd/
famiROM
Удобный сплиттер ROM файлов (NES-> PRG BIN, CHR BIN).
Сам обрезает iNES, наращивает до нужных размеров. Один клик и можно прошивать флешку.
Также есть и редактор заголовка, и еще куча всего, чего я пока не понимаю.
Ветка автора: http://nintendoage.com/forum/messageview.cfm?catid=22&threadid=63070
DISASM6
Хороший дизассемблер. Работает с CDL файлами статистики Code/Data Logger эмулятора FCEUX.
Красиво оформляет код для ASM6.
https://forums.nesdev.com/viewtopic.php?t=7466
Извините, если это очевидные тулзы, но мало ли кому пригодится.
Omkommersind:
Эмулятор FCE220 имеет потрясающий набор встроенных инструментов для интерактивной отладки, есть ли какие-нибудь схожие аналоги для ромов SMD/Genesis?
MetalliC:
Omkommersind, вагон - Exodus, Gens Kmod, RegenD
или Smd IDA Tools
Навигация
Перейти к полной версии