Это не домашняя проблема, это для игры, которую я разрабатываю.
У меня есть два 16-битных цвета RGB, и я хотел бы изменить их шесть каналов в соответствии с шестью другими четырьмя битами. Алгоритм прост, но утомителен; Я ищу способ оптимизировать его, сделав более полезную работу сразу.
Обзор высокого уровня:
-
hl
указывает на четыре цветовых байта.[hl] = %gggrrrrr
,[hl+1] = %0bbbbbgg
,[hl+2] = %GGGRRRRR
и[hl+3] = %0BBBBBGG
. (Это два цвета,rgb
иrgb
.) -
bc
указывает на три байта дельта.[bc] = %hhhhaaaa
,[bc+1] = %ddddssss
и[bc+2] = %ppppqqqq
. (Это шесть значений дельта,h
,a
,d
,s
,p
иq
.) - Таким образом, существует шесть значений 5-битного цветового канала и шесть 4-битных значений дельта. Я хочу связать каждый цветовой канал C с дельта-значением D и изменить C следующим образом: C '= C + ( D и% 11) - (( D и% 1100) → 2), но сохраняя C в пределах своих 5-битных границ [0, 31]. Мне все равно, как они спарены: любое удобное соединение "один-к-одному" прекрасно. И если C + (( D и% 1100) → 2) - ( D и% 11) позволяет более элегантный алгоритм, Я был бы в порядке с этим.
Если я изолирую цветной канал C в регистре d
и значение дельта D в регистре e
, то эта процедура будет делать изменение для этой пары
VaryColorChannelByDV:
; d = color, e = DV
; a <- d + (e & %11) - (e >> 2), clamped to [0, 31]
ld a, e
and %11 ; a <- (e & %11)
add d ; a <- d + (e & %11)
srl e
srl e ; e <- e >> 2
sub e ; a <- d + (e & %11) - (e >> 2)
jr c, .zero ; a < 0, clamp to 0
cp 32
ret c ; 0 <= a < 32
ld a, 31 ; a >= 32, clamp to 31
ret
.zero
xor a
ret
До сих пор у меня есть общая подпрограмма, которая применяет любой DV к любому цветному каналу; затем три процедуры, которые изолируют красные, зеленые или синие каналы и применяют к ним данный DV; и, наконец, основная процедура, которая выбирает шесть DV и вызывает с ними соответствующую процедуру изменения канала. Это "достаточно хорошо", но я уверен, что есть место для улучшения. Скорость выполнения не кажется проблемой, но я бы хотел уменьшить размер кода (и, конечно, удаление избыточных инструкций также немного улучшит скорость). Есть ли какие-либо хитроумные манипуляции с использованием asm, которые помогут?
Здесь полный код:
GetColorChannelVariedByDV:
; d = color, e = DV
; a <- d + (e & %11) - (e & %1100 >> 2), clamped to [0, 31]
ld a, e
and %11
add d
srl e
srl e
sub e
jr c, .zero
cp 32
ret c
ld a, 31
ret
.zero
xor a
ret
VaryRedByDV:
;;; e = DV
;;; [hl+0] = gggr:rrrr
;;; [hl+1] = 0bbb:bbgg
; store red in d
ld a, [hl]
and %00011111
ld d, a
; vary d according to e
call GetColorChannelVariedByDV
; store a back in red
ld d, a
ld a, [hl]
and %11100000
or d
ld [hl], a
ret
VaryGreenByDV:
;;; e = DV
;;; [hl+0] = gggr:rrrr
;;; [hl+1] = 0bbb:bbgg
; store green in d
ld a, [hli]
and %11100000
srl a
swap a
ld d, a ; d = 00000ggg
ld a, [hld]
and %00000011
swap a
srl a
or d
ld d, a
; vary d according to e
call GetColorChannelVariedByDV
; store a back in green
sla a
swap a
ld d, a
and %11100000
ld e, a
ld a, d
and %00000011
ld d, a
ld a, [hl]
and %00011111
or e
ld [hli], a
ld a, [hl]
and %11111100
or d
ld [hld], a
ret
VaryBlueByDV:
;;; e = DV
;;; [hl+0] = gggr:rrrr
;;; [hl+1] = 0bbb:bbgg
; store blue in d
inc hl
ld a, [hl]
and %01111100
srl a
srl a
ld d, a
; vary d according to e
call GetColorChannelVariedByDV
; store a back in blue
ld d, a
sla d
sla d
ld a, [hl]
and %10000011
or d
ld [hl], a
dec hl
ret
VaryColorsByDVs::
; hl = colors
; [hl+0] = gggr:rrrr
; [hl+1] = 0bbb:bbgg
; [hl+2] = GGGR:RRRR
; [hl+3] = 0BBB:BBGG
; bc = DVs
; [bc+0] = hhhh:aaaa
; [bc+1] = dddd:ssss
; [bc+2] = pppp:qqqq
;;; LiteRed ~ hDV, aka, rrrrr ~ hhhh
; store hDV in e
ld a, [bc]
swap a
and %1111
ld e, a
; vary LiteRed by e
call VaryRedByDV
;;; LiteGrn ~ aDV, aka, ggggg ~ aaaa
; store aDV in e
ld a, [bc]
and %1111
ld e, a
; vary LiteGrn by e
call VaryGreenByDV
;;; move from h/a DV to d/s DV
inc bc
;;; LiteBlu ~ dDV, aka, bbbbb ~ dddd
; store dDV in e
ld a, [bc]
swap a
and %1111
ld e, a
; vary LiteBlu by e
call VaryBlueByDV
;;; Move from Lite color to Dark color
inc hl
inc hl
;;; DarkRed ~ sDV, aka, RRRRR ~ ssss
; store sDV in e
ld a, [bc]
and %1111
ld e, a
; vary DarkRed by e
call VaryRedByDV
;;; move from d/s DV to p/q DV
inc bc
;;; DarkGrn ~ pDV, aka, GGGGG ~ pppp
; store pDV in e
ld a, [bc]
swap a
and %1111
ld e, a
; vary DarkGrn by e
call VaryGreenByDV
;;; DarkBlu ~ qDV, aka, BBBBB ~ qqqq
; store qDV in e
ld a, [bc]
and %1111
ld e, a
; vary DarkBlu by e
call VaryBlueByDV
ret