News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

Could you list some horizontal flipping techniques?

Started by ssr86, 18:08, 24 November 14

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

ssr86

Like in the subject...

The only one I can think of (other than having a dedicated routine for flipping the sprite before drawing if needed) is having a lookup table for each byte with it's flipped equivalent  and changing every "inc screen_pointer" with "dec screen_pointer" (or having two drawing functions).
Could store all sprite "half-flipped" (what I mean is - even lines are normal and odd are flipped) then during the drawing only half of the lines would need to be flipped.
However these seem only applicable for "standard sprites"... What about compressed? One possibility would be to have all the pixel code handling procedures in two versions.. but that doesn't sound too good...

Tried to google some techniques but couldn't find anything...
and so I decided to ask here...

arnoldemu

Quote from: ssr86 on 18:08, 24 November 14
Like in the subject...

The only one I can think of (other than having a dedicated routine for flipping the sprite before drawing if needed) is having a lookup table for each byte with it's flipped equivalent  and changing every "inc screen_pointer" with "dec screen_pointer" (or having two drawing functions).
Could store all sprite "half-flipped" (what I mean is - even lines are normal and odd are flipped) then during the drawing only half of the lines would need to be flipped.
However these seem only applicable for "standard sprites"... What about compressed? One possibility would be to have all the pixel code handling procedures in two versions.. but that doesn't sound too good...

Tried to google some techniques but couldn't find anything...
and so I decided to ask here...
For uncompressed sprites I have two functions and a lookup table that converts the pixels to reversed.
I do this per byte and draw in reverse exactly as you describe here.

I know that Renegade stores the bytes normal, reversed, normal, reversed, perhaps it balances the drawing better because the sprites can face either direction. Gryzor however stores them all facing the same direction because I guess the direction is pretty much the same and the directions do not change much.

Doing it on a line by line basis is interesting and I haven't tried it.

Reversing a compressed sprite should be the same idea, draw in reverse and swap the bytes???

A compiled sprite would need a seperate function to reverse it.




My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ssr86

Thank you
Quote
Reversing a compressed sprite should be the same idea, draw in reverse and swap the bytes???
The problem for me is that I use a couple of code handling procedures for dealing with the drawing of pixel chunks of the same type (opaque, masked, transparent with a couple of versions for other sizes of chunks), so I think that to make the sprite be drawn in reverse would take me too much cputime  to switch the increments to decrements... and having every chunk drawing routine in two versions doesn't seem too good too. For a "simple" opaque-masked-transparent compression (with 1 procedure for opaque, maybe 2 for masked and 1 for skipping transparent bytes) the flipping would be the same as for uncompressed sprites.

Ast

Try this if it could Help you. This is only working for Cpc Plus sprite, but you can easily convert it for Cpc sprites format. All you have to know is How are coded pixels on Cpc because, they're not linear as on Cpc plus sprites and depend of Cpc screen resolution.
_____________________

Ast/iMP4CT. "By the power of Grayskull, i've the power"

http://amstradplus.forumforever.com/index.php
http://impdos.wikidot.com/
http://impdraw.wikidot.com/

All friends are welcome !

arnoldemu

Quote from: ssr86 on 19:41, 24 November 14
Thank youThe problem for me is that I use a couple of code handling procedures for dealing with the drawing of pixel chunks of the same type (opaque, masked, transparent with a couple of versions for other sizes of chunks), so I think that to make the sprite be drawn in reverse would take me too much cputime  to switch the increments to decrements... and having every chunk drawing routine in two versions doesn't seem too good too. For a "simple" opaque-masked-transparent compression (with 1 procedure for opaque, maybe 2 for masked and 1 for skipping transparent bytes) the flipping would be the same as for uncompressed sprites.

Ast presented a method where each line is put into a buffer, this can then be reversed and put onto the screen, but that involves extra time too.

I think any method chosen will take a little extra time.

Rhino mentioned an interesting idea. He was saying to work out which direction is the most common and code the optimised version for that. For the less common direction use another method or a less optimised method.

Reversing a sprite means -ve for fetching pixels or data and not on the screen, we still draw left to right on the screen.

Another thing to consider...

Lets take a made up first line:

skip x, mask 1 byte, write 20 opaque bytes, mask 1 byte, end of line (y more bytes to right side of sprite's rectangle)

in reverse this becomes, skip y bytes, mask 1 byte, write 20 opaque bytes, mask 1 byte.. now we need to end the line here and not do the skip x. So the data is different in reverse too, unless we modify it in a way that we can compensate for the skip/end of line.

So we need to find a way to encode the skip and end of line so we can reverse the data. The masking and opaque bytes need their pixels swapping using a look up table.


One way is this:
;; HL = screen
;; DE = pixel data (not reversed)
e.g.

B = &100 = reverse pixels, &200=mask bytes

ld a,(de) ;; read normal pixels
ld c,a
ld a,(bc) ;; read reversed
ld c,a
inc b
ld a,(bc) ;; read mask
dec b
and (hl) ;; mask onto screen
or c
ld (hl),a
inc hl
dec de


Ast's plus sprite example is good, but it doesn't consider the skip/end of line for compressed sprites, and moving the pixels in the sprite ram of course doesn't affect the screen.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Ast

Quote from: arnoldemu on 10:03, 25 November 14

I think any method chosen will take a little extra time.

Arnoldemu, you're right. In my mind, the best way would be to store all datas sprites in all positions in Ram. 128kb  is not enough ? Remember now TOtO (Bryce...etc) proposed us the x-mem.... So...
It Will be easy to display the sprite in any way you want. You just have to change the sprite data adress.
Using this way means using the same machine time for all sprites. (-:
_____________________

Ast/iMP4CT. "By the power of Grayskull, i've the power"

http://amstradplus.forumforever.com/index.php
http://impdos.wikidot.com/
http://impdraw.wikidot.com/

All friends are welcome !

arnoldemu

Quote from: Ast on 18:03, 25 November 14
Arnoldemu, you're right. In my mind, the best way would be to store all datas sprites in all positions in Ram. 128kb  is not enough ? Remember now TOtO (Bryce...etc) proposed us the x-mem.... So...
It Will be easy to display the sprite in any way you want. You just have to change the sprite data adress.
Using this way means using the same machine time for all sprites. (-:

I don't think 128KB is enough if you have many frames or many different sprites AND if you want to have lots of music and other data at the same time.

Yes the x-mem with 512KB ram would make this much easier.

Yes, the same time for all sprites. All could be compiled sprites and at different pixel positions needed.

My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ssr86

Sorry I'm silent, but I'm a slow-thinker and I'd like to try a thing or two...
However still don't really know a good way to flip compressed sprites... Maybe make the sprites always have at least one transparent pixel on the left and right ("margins"). Give them different control codes and when drawing with jump table use two tables... One would be using right margin as "skip", left as "end of line" and in the other table roles would be reversed... Or just change (self modifying code) the jump addresses in the table for these two...then only one table would be enough.
For the call list technique I could maybe make the "margins" as jumps to either "skip" or "end of line" routines and using self modifying code to change the jump address.
Just these "quick" ideas so far...

I remember reading this article (many years ago) about design decisions on zx spectrum target renegade (thought that the cpc version was more or less a speccy port so I could read obout the flipping technique arnoldemu mentioned...but these are two different games). So in the spectrum version they stored only right versions of sprites in memory and used the lookup table to get the left versions when drawing...

btw: anyone knows about similar articles about cpc games?

Quote
Reversing a sprite means -ve for fetching pixels or data and not on the screen, we still draw left to right on the screen.
:-[

EDIT:
I just remembered that I read about the planned Parasol Stars cpc port and cngsoft is thinking about using compressed sprites...
To cngsoft: how are you planning to do horizontal flipping of the sprites?
(Parasol Star Remake ? (PS4CPC))

ssr86

Here's a code example for flipping a standard sprite "in place".
You would need to call this only when the sprite changes it's facing direction.
This method doesn't need an additional buffer.
The sprite data is stored normally.

It should be good in cases when the sprite seldom changes the direction it is facing.
Maybe when the direction of the sprite's standing
pose changes only after a jump then the sprite data for the standing animation could be reversed during the jumping animation...

I don't know how this compares to other methods...

The code is written for sprites with even width but can be modified to work with odd widths too...
Here there's a byte pair in the middle and for the odd version there would be a single byte to reverse.
The values to add to hl and de to get to the next line of the sprite data would slightly change too.


The lookup table at the end is for mode0 pixels.


flip_sprite:
                     ld b,reverse_table/256                                          ;; bc is used for accessing the reversed byte values
                     ld de,sprite_data+sprite_width_in_bytes-1                       ;; de = address of the end of the first sprite data line
                     ld hl,sprite_data                                               ;; hl = address of the sprite data

                     exx
                     ld b,sprite_height
flip_line_loop:
                     exx

                     ld a,(de)          ;; get left byte
                     ld c,a             ;;
                     ld a,(bc)          ;; reverse left byte
                     ex af,af'
                     ld c,(hl)          ;; get right byte
                     ld a,(bc)          ;; reverse it
                     ld (de),a          ;; store it on the left side of the sprite data line
                     ex af,af'
                     ld (hl),a          ;; store the left byte on the right side of the sprite data line
                     
                     dec e              ;; here I assume that the sprite data is aligned (not necessarily page aligned)
                     inc l              ;;

                     ;; repeat the part above k = (sprite_width_in_bytes / 2 - 1 ) times
[...]
                     ;; for the last pair of bytes in the line loop we can drop the dec/inc part:

                     ld a,(de)
                     ld c,a
                     ld a,(bc)
                     ex af,af'
                     ld c,(hl)
                     ld a,(bc)
                     ld (de),a
                     ex af,af'
                     ld (hl),a       

                     ;; get to the next sprite data line

                     ld a,l
                     add a,sprite_width_in_bytes/2+1
                     ld l,a
                     adc a,h
                     sub l
                     ld h,a

                     ld a,e
                     add a,3*sprite_width_in_bytes/2-1
                     ld e,a
                     adc a,d
                     sub e
                     ld d,a

                     exx
                     djnz  flip_line_loop

                     exx
                     ret

org ($/256+1)*256
reverse_table:
db $00,$02,$01,$03,$08,$0A,$09,$0B,$04,$06,$05,$07,$0C,$0E,$0D,$0F
db $20,$22,$21,$23,$28,$2A,$29,$2B,$24,$26,$25,$27,$2C,$2E,$2D,$2F
db $10,$12,$11,$13,$18,$1A,$19,$1B,$14,$16,$15,$17,$1C,$1E,$1D,$1F
db $30,$32,$31,$33,$38,$3A,$39,$3B,$34,$36,$35,$37,$3C,$3E,$3D,$3F
db $80,$82,$81,$83,$88,$8A,$89,$8B,$84,$86,$85,$87,$8C,$8E,$8D,$8F
db $A0,$A2,$A1,$A3,$A8,$AA,$A9,$AB,$A4,$A6,$A5,$A7,$AC,$AE,$AD,$AF
db $90,$92,$91,$93,$98,$9A,$99,$9B,$94,$96,$95,$97,$9C,$9E,$9D,$9F
db $B0,$B2,$B1,$B3,$B8,$BA,$B9,$BB,$B4,$B6,$B5,$B7,$BC,$BE,$BD,$BF
db $40,$42,$41,$43,$48,$4A,$49,$4B,$44,$46,$45,$47,$4C,$4E,$4D,$4F
db $60,$62,$61,$63,$68,$6A,$69,$6B,$64,$66,$65,$67,$6C,$6E,$6D,$6F
db $50,$52,$51,$53,$58,$5A,$59,$5B,$54,$56,$55,$57,$5C,$5E,$5D,$5F
db $70,$72,$71,$73,$78,$7A,$79,$7B,$74,$76,$75,$77,$7C,$7E,$7D,$7F
db $C0,$C2,$C1,$C3,$C8,$CA,$C9,$CB,$C4,$C6,$C5,$C7,$CC,$CE,$CD,$CF
db $E0,$E2,$E1,$E3,$E8,$EA,$E9,$EB,$E4,$E6,$E5,$E7,$EC,$EE,$ED,$EF
db $D0,$D2,$D1,$D3,$D8,$DA,$D9,$DB,$D4,$D6,$D5,$D7,$DC,$DE,$DD,$DF
db $F0,$F2,$F1,$F3,$F8,$FA,$F9,$FB,$F4,$F6,$F5,$F7,$FC,$FE,$FD,$FF

arnoldemu

#9
I have a method that will reverse any width sprite. I'll post it up for comparison.

So back to compressed sprites.

I agree with you.

The only thing to add is we need a skip code for the middle of the sprite.

Example:

left skip/end-left, mask pixels, draw opaque, mask pixels, skip pixels, mask pixels, draw opaque, mask pixels, right skip/end-right.

this is all the combinations in an example sprite.

Using these as we both talked about, you can draw in both directions, reversing pixel data where needed to draw a compiled sprite in normal or reversed.

I have just thought of a problem, if we are reading this data in reverse, we need an extra code which is the number of pixels to the next line.
Or, if we plan to clip the sprites vertically, a list of lines, so we can choose how many to skip to clip the sprite.

EDIT: I have methods for drawing a sprite shifted and one for drawing a reverse sprite shifted.

4 different drawing functions! ;)

EDIT2: Interesting. I see ssr86 you have presented a method of reversing the sprite's pixel data in it's buffer.
I have a method that doesn't do that, it reads the data, reverses it and writes it to the screen. The sprite data is not modified.

My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

arnoldemu

#10
;; this will flip the graphics before drawing


;; draw right to left and flip graphics as we do that
;; plot sprite unshifted with mask

;; HL = screen address (adjusted to right side)
;; DE = pixel data (not adjusted)
;; IXL = width
;; IXH = height
mask_sprite_rev:
push ix
push hl
;; X loop
mask_spriter1a:
ld a,(de)                ;; get pixel data
ld c,a
ld b,rev_table/256        ;; look up to reverse it
ld a,(bc)
ld c,a
ld b,mask_table/256        ;; get mask
ld a,(bc)                           
and (hl)                           
or c                               
ld (hl),a   
dec hl                ;; drawing
inc de

defb &dd
dec l
jr nz,mask_spriter1a
pop hl
call scr_next_line
pop ix
defb &dd
dec h
jr nz,mask_sprite_rev
ret



NOTE: The mask is derived from the pixels here, so this means that if you reverse the pixels you then look up a reversed mask too.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

arnoldemu

#11
We don't need eol codes for left and right side.

We use the same data, but draw to the screen in reverse and reverse the pixels and mask as we draw.
That will work.

Untested example!


;; follows the data forwards

;; drawing reversed following data backwards

;;------------------------------------------------------------------------------------
;; HL = screen of left side
;; DE = pixel data

draw_sprite:
push hl
call draw
pop hl
ld a,(de)        ;; end code for sprite
or a
ret z
call scr_next_line
jr draw_sprite

;;------------------------------------------------------------------------------------

draw:
ld a,(de) ;; get code
ld c,a
and &c0
or a
inc de
ret z
cp &40
jr z,trans            ;; fully transparent
cp &80
jr z,mask            ;; masked

;; fully opaque

;; count
ld a,c
and &3f
ld c,a

opaque:                ;; opaque
;; opaque
ld a,(de)
ld (hl),a
inc de
inc hl
dec c
jr nz,opaque
jp draw

;; fully transparent
trans:
;; number of bytes to skip
ld a,c
and &3f
ld c,a
ld b,0
add hl,bc ;; add onto screen

jp draw

;; masked
mask:
ld a,c
and &3f
ld c,a

mask1:
ld a,(de)        ;; read mask
and (hl)        ;; mask on screen
inc de
ld a,(de)        ;; read pixels
or (hl)            ;; or on pixels
inc de
ld (hl),a        ;; back to screen
inc hl            ;; increment screen
inc de            ;; increment pixels
dec c
jr nz,mask1        ;; loop

jp draw



;;------------------------------------------------------------------------------------
;; HL = screen adjusted to right side
;; DE = pixel data
;; IXH = reverse table
draw_sprite_r:
push hl
call draw_r
pop hl
ld a,(de)            ;; end code for sprite
or a
ret z
call scr_next_line
jr draw_sprite_r

;;------------------------------------------------------------------------------------

drawr:
ld a,(de) ;; get code
ld c,a
and &c0
or a
inc de
ret z
cp &40
jr z,transr            ;; fully transparent
cp &80
jr z,maskr            ;; masked

;; fully opaque

;; count
ld a,c
and &3f
ld c,a

opaquer:                ;; opaque
;; opaque
ld a,(de)

;; reverse it
defb &dd
ld a,(ix+0)

ld (hl),a
inc de
dec hl                    ;; decrement screen
dec c
jr nz,opaque

jp draw

;; fully transparent
transr:
;; number of bytes to skip
ld a,c
and &3f
ld c,a
ld b,0
or a
sbc hl,bc    ;; go back x bytes
jp draw

;; masked
maskr:
ld a,c
and &3f
ld c,a

mask1:
ld a,(de)        ;; read mask
defb &dd
ld l,a
ld a,(ix+0)        ;; reverse mask
and (hl)        ;; mask on screen
inc de

ld a,(de)        ;; read pixels
defb &dd
ld l,a
ld a,(ix+0)        ;; reverse it

or (hl)            ;; or on reversed pixels
inc de
ld (hl),a        ;; back to screen
dec hl            ;; decrement screen
inc de            ;; increment pixels
dec c
jr nz,mask1        ;; loop

jp drawr






This method doesn't use the jump table.

I have a code byte that has the top two bits defining code:

0 = end of line (if we see 0 at the start of the line it means end of sprite)
&40 = transparent/skip
&80 = masked
&c0 = opaque

lower bits are a count.

I have implemented the forwards and reverse code.

See how HL is adjusted to move backwards.
And I use a table to convert pixels and mask from forward to reverse.

This will work.

EDIT:

How does this translate to your call method for drawing I don't know because I've not used that method.
It almost seems a form of compiled sprites and not compressed.

Are the calls formed in the data?

Is it like this???

defw transparent_3
defw opaque_1
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

ssr86

Wow, thanks :D . This is a lot for me to process... (like I said before - I'm quite slow)
I think I will have to modify my code for compressed sprites looking at the code proposed by you and by the code by cngsoft in his parasol stars post...

Quote
How does this translate to your call method for drawing I don't know because I've not used that method.
It almost seems a form of compiled sprites and not compressed.
I look at these as compressed sprites with control codes saved as calls or return addresses (and the sprite data consists of things like: number of bytes to skip/draw, bytes to draw, mask value, etc).
These were proposed by ralferoo and he called this approach "a hybrid method", so yes...these aren't pure compressed sprites... Here's his post:
Help - algorith for optimal sequence of codes (drawing compressed sprites)

Quote from: ralferoo on 15:18, 22 December 13
Firstly, just because it might help you if you're searching for further stuff online, what you call "wage" is usually called "cost" in English papers on this subject.

But yeah, this is the fundamental problem you need to solve whatever the compression problem, but usually the required amount of backtracking is small. Because it's usually "is a run of literals 'better' than a run of literals, some compression, another run of literals".  Your problem is still to vague to make that decision, because you're still thinking too abstractly, from what I understand (although I don't really understand your terminology).

For example, consider 8-bit run-length encoding. You have a byte that contains a "length from 0-127" and a bit that dete rmines between having n literals or the "next byte" n times. In that case, the encoding "123444567" could be (3,"123"),(3x"4"),(3,"567") or 4+2+4 bytes. The same could also be encoded as (9,"123444567") which is also 10 bytes longer. For this example, you could chose either.

However, "1234444567" looks like you'd always want to encode it as (3,"123"),(4x"4"),(3,"567") which is 10 bytes again rather than the literal which would be 10 bytes. However, depending on the data that follows, that might sway your decision. If the next block would be a literal block, it'd make sense to continue the literal block as the extra byte in the preceding literal would be smaller than the overhead to change coding, if it's a repeated block there's no advantage to using the longer literal version and so we can use the shorter form.

But, if we had a 16-bit RLE, the decision point shifts because there's an increased overhead to change encoding method. Likewise, if you're using a dynamic huffman-style there's probably less cost to changing mode.

The only thing you can really do is to work out what you're actually going to generate and again, as I said, what you're actually optimising for. If it's execution time, you'd want to consider that e.g. INC L takes 1 cycle, LD A,(DE): INC E:LD (HL),A:INC L takes 6 cycles. However, a conditional jump would take 2 or 3 cycles, a DJNZ would take 3 or 4 cycles and so a loop of INC E:DJNZ x is actually almost as slow as copying a literal. The copy loop is even slower as you're now taking 10 cycles per byte. Obviously, you've saved storage space, so if this is your only aim, do it like this. Whatever the shorter representation of data is wins.

On something like the CPC though, the speed is important. Even more so with a large sprite like you're using. So, as we said, the quickest approach is to create a function that plots all the pixels.

A better approach is a hybrid. You could create a list of functions that do a few things, e.g.

skip2: INC L: INC L: RET (5 cycles)
skip3: INC L: INC L: INC L: RET (6 cycles)
copy2: LD A,(DE): INC E:LD (HL),A:INC L: LD A,(DE): INC E:LD (HL),A:INC L: RET (15 cycles)
copyskipcopy: LD A,(DE): INC E:LD (HL),A:INC L: LD A,(DE): INC E:LD (HL),A:INC L: INC L:LD A,(DE): INC E:LD (HL),A:INC L: LD A,(DE): INC E:LD (HL),A:INC L RET (28 cycles)
nextline: LD BC,#800-width: ADD HL,BC: RET (9 cycles)
nextline_skip1: LD BC,#801-width: ADD HL,BC: RET (9 cycles)

etc... These then could become your building blocks and you can then make a list of them. The simplest might be:

CALL copy2:CALL skip3:CALL copy2:CALL nextline (20 cycles + 15+6+16+9)

You could even be more clever, and put these on the stack, e.g.:

LD (sp_restore+1),SP ;6
DI ;1
LD SP,sprite1 ;4
RET ;3
sp_restore: LD SP,0 ;4
EI ;1
RET ;3

DEFW sp_restore
DEFW nextline, copy2, skip3, copy2 (15+6+16+9)

So, the overhead is bigger per sprite, but the per-operation cost is 5 cycles lower. As the overhead is 25 cycles, this cost is recouped after 5 blocks. The advantage with this approach is you can have special functions. e.g. storing a fixing byte value a couple of times rather than loading it from DE.

As I said before, this is something you need to work out for yourself, but that determination can only really be made once you know the sprite data you're copying - e.g. does it have a lot of gaps or blocks of static colour? Are there repeated elements? But most importantly, which do you value most, speed or space?

sigh

Silly question time:

Is it possible for you to have your sprite uncompressed in one direction and then compressed in another direction? This way it could balance the CPU versus the RAM issue?



andycadley

Quote from: sigh on 12:51, 29 November 14
Silly question time:

Is it possible for you to have your sprite uncompressed in one direction and then compressed in another direction? This way it could balance the CPU versus the RAM issue?
I think it would be better to store the compressed sprite half flipped, that way your drawing routine only ever has to deal with writing half of it flipped and does it regardless of the direction the sprite is facing, which means you're more likely to get a consistent draw time overall.

arnoldemu

Quote from: andycadley on 12:14, 30 November 14
I think it would be better to store the compressed sprite half flipped, that way your drawing routine only ever has to deal with writing half of it flipped and does it regardless of the direction the sprite is facing, which means you're more likely to get a consistent draw time overall.
Exactly as renegade does it and I believe exactly for the reason you describ.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Ast

Exactly what i say in my post. if you only use this way to calculate all your sprites, you Will win a lot of machine time which can be used to display more or bigger sprites on your screen.
_____________________

Ast/iMP4CT. "By the power of Grayskull, i've the power"

http://amstradplus.forumforever.com/index.php
http://impdos.wikidot.com/
http://impdraw.wikidot.com/

All friends are welcome !

Carnivius

Quote from: Ast on 13:09, 30 November 14
Exactly what i say in my post. if you only use this way to calculate all your sprites, you Will win a lot of machine time which can be used to display more or bigger sprites on your screen.

This is interesting to me.  :)
Favorite CPC games: Count Duckula 3, Oh Mummy Returns, RoboCop Resurrection, Tankbusters Afterlife

Ast

You can also ask for TFM's technics used in his famous "Cyber Chicken" game. Many and many sprites have been displayed in the Cpc screen. I suppose all Chicken's zoom haven't been made in real Time, but they are all loaded in the memory.  Try this way instead.
_____________________

Ast/iMP4CT. "By the power of Grayskull, i've the power"

http://amstradplus.forumforever.com/index.php
http://impdos.wikidot.com/
http://impdraw.wikidot.com/

All friends are welcome !

ssr86

Quote from: andycadley on 12:14, 30 November 14
I think it would be better to store the compressed sprite half flipped, that way your drawing routine only ever has to deal with writing half of it flipped and does it regardless of the direction the sprite is facing, which means you're more likely to get a consistent draw time overall.

What is the best way to store the half-flipped sprite?

I'm currently experimenting (still stuck with standard sprites...) with storing the sprite's even lines normally and the odd lines flipped (because of what I've written in my first post...but haven't really thought about it before writing) . I store these somewhat as seperate sprites and draw them as such...Then I need two drawing subroutines - one for drawing a half-sprite (only odd or only even lines) normally and one for drawing it reversed....I don't think there's any time gain in comparison with storing e.g. the entire first half of the sprite lines normal and the other half flipped...
The method I'm trying is actually the lesser choice if the y-movement step is 2 lines... As i can't use set/res 3,x to get to the next line (so the first half of the lines nomral, rest reversed is visibly better). I haven't thought how it works when you store the sprite as normal byte, reversed byte... seems somewhat more complicated to me... :-[

How would the target renegade storing method (bytes: normal - reversed -normal...) work in the case of compressed sprites? It would be somewhat the same as standard sprites if you have only codes for skip n, 1 draw 1 byte masked, draw 1 byte opaque. What if you have codes for entire groups of same type bytes? Would it be that you store the sprite group normal, group reversed?

andycadley

I'm not sure compression needs to make much of a difference, you still read the bytes as if going left to right but write them right to left flipping along the way. It really just comes down to register usage at that point. Whether you store alternate lines flipped or split it into top/bottom half is probably more a matter of style than anything. Although using the top/bottom split probably makes it quicker to draw sprites that never need flipping without having a separate routine.

ssr86

Quote from: andycadley on 19:57, 30 November 14
I'm not sure compression needs to make much of a difference, you still read the bytes as if going left to right but write them right to left flipping along the way. It really just comes down to register usage at that point.
... :-[ I already forgot about arnolemu's solution...
Quote from: arnoldemu on 14:46, 28 November 14
We use the same data, but draw to the screen in reverse and reverse the pixels and mask as we draw.
That will work.

Quote from: andycadley on 19:57, 30 November 14
Whether you store alternate lines flipped or split it into top/bottom half is probably more a matter of style than anything. Although using the top/bottom split probably makes it quicker to draw sprites that never need flipping without having a separate routine.
I thought that, because of the cpc's screen memory layout, storing the sprite as even lines normal, odd lines reversed would open some options for optimization...but It seems to not be the case...

Although I don't think that I would need a separate routine for sprites that never need flipping with the even-odd lines storing method. I have one subroutine for drawing a set of lines normally and other for drawing it flipped (so "just" 2 subroutines in total). The actual drawing routine checks the facing direction of the sprite, loads the pointers, calls the draw_normal or  draw reversed, changes the pointers and calls the other drawing subroutine. I think there wouldn't be any difference for sprites that aren't flipped... You could just call the draw_normal subroutine 2 times.

pacomix

Depending the type of game you are doing, number of frames, sprites, need of drawing with flipped (in any direction) you should decide the best approach.


For me the best one and general way was the last modification I did for CPC Bros, but surely it can be applied to almost any game resulting in a good balance between speed and memory consumption, is the next.


Basically consists of having in the structure that defines the frame the amount of frames that will be drawn in this frame mirrored/not mirrored and also a flag to know if the last time the frame was drawn mirrored or not.


If the last state of the frame was mirrored, start drawing all the sprites that are using the mirrored frame. If there is a sprite using the non-mirrored version then flip the frame data "IN PLACE", update the flag accordingly and draw the non-mirrored sprites, otherwise leave it untouched.


The advantage of everything is that you only need two routines (the flip in place and the normal drawing routine). The more sprites and the less frames each one the better benefits you get from this.


With this method we save one or more specific drawing routines for mirrored sprites (horizontal/vertical) as we can use the normal one. Apart of that, there is usually the overhead of having to mirror the data to be drawn for every drawcall we do in case we have specific routines for drawing mirrored sprites.


Remember The way of the Donkey.

ssr86

Here are a few methods that I could think of for horizontal flipping, vertical flipping, horizontal shifting and 180 degree rotation of standard sprites.
All examples use mode 0 masked sprites. Masking done via lookup table. The sprite is 24x22 pixels (so 12x22 bytes).

These examples were more or less written a couple of months back...
Hope someone finds this useful...
Has someone got any other ideas?

I also wanted to cover compressed and compiled sprites but lost all the motivation along the way...
Also the code can be a little messy sometimes but I've tried to make "clean"...

I will start with vertical flipping:

vflip1.asm
uses "in-place" method, which means that the sprite is stored in original version and a routine is called to flip the sprite data. This means that the flip is "permanent" and we need to reflip the data to draw the sprite in the original form.
timings:
draw normal: 5987 nops
flip: 1844 nops
---------------------------------------------
vflip2.asm
uses "buffer" method, similar to the above but we don't change the original data, instead we store the flipped version in a buffer. So to draw the flipped version we call the normal draw routine but for the data in the buffer.

timings:

vflip2.asm (using ldi to copy data)
draw normal: 5981 nops
flip: 1568 nops

vflip2b.asm (using stack to copy data)
draw normal: 5981 nops
flip: 1746 nops
---------------------------------------------
vflip3.asm
uses "during drawing" method, we do the flipping while drawing the sprite.
The few versions are basically the same but differ in the directions of drawing to screen or fetching sprite data (these are not all of the possibilities) in the flip routine.

draw normal: 5981 nops

vflip3a:
draw flipped: 6098 nops
vflip3b:
draw flipped: 6057 nops
vflip3c:
draw flipped: 6105 nops
vflip3d:
draw flipped: 6013 nops
----------------------------------------------
other timings:
erase sprite: 1915 nops

Note however that the examples are not fully optimised (only some loop unrolling has been done...) so the timings in the vflip3 examples are not really comparable, I guess...

ssr86

Horizontal flipping:

hflip1.asm is using "in-place" method
timings:
draw normal: 5978 nops
flip: 2866 nops
-------------------------------------
hflip2.asm is using "buffer" method
timings:
draw normal: 5972 nops
flip: 2885 nops
-------------------------------------
hflip3a.asm is using "during drawing" method with no partial preflipping of sprite data
timings:
draw normal: 5976 nops
draw flipped: 7161 nops
-------------------------------------
hflip3b.asm is using "during drawing" method with "normal_byte-reversed_byte" partial preflipping of sprite data
timings:
draw normal: 6510 nops
draw flipped: 6541 nops
-------------------------------------
hflip3c.asm is using "during drawing" method with "normal_line-flipped_line" preflipping of sprite data
timings:
draw normal: 6327 nops
draw flipped: 6337 nops
-------------------------------------
hflip3c-2.asm is using "during drawing" method with "normal_line-flipped_line" preflipping of sprite data
Alternative to hflip3c but here, instead of having two routines where each iteration of the inner loop draws two lines (one normal, one flipped), we have routines for drawing a normal line and a flipped line and calls them interchangeably in the sprite loop (to save some memory)...
timings:
draw normal: 6501 nops
draw flipped: 6511 nops
------------------------------------
hflip3d.asm is using "during drawing" method with "half_normal-half_flipped" preflipping of sprite data
timings:
draw normal: 6560 nops
draw flipped: 6563 nops

Some thoughts:

1. I think that the "buffer" method would be best for sprite data decompession - you load only data facing one direction and then use the flip routine to obtain the flipped versions of the sprites.
2. With the "in-place" method you only pay for the change in direction the sprite faces, so should be good when the sprites seldom change the directions they face.
3. If you alternate a lot between the two versions then the best would be to use one of the 3b-3d versions (these have evened timings compared to 3a)

Powered by SMFPacks Menu Editor Mod