forked from KolibriOS/kolibrios
Ivan Baravy
459cb3ec5b
* Support grayscale images with alpha channel. * Support rgb images with transparent color. * Process 'tRNS' chunk (simple transparency). Transparent png images were already supported for color type 6, which is RGBA. Now indexed images (color type 3) can also have transparency via 'tRNS' chunk data. git-svn-id: svn://kolibrios.org@8449 a494cfbc-eb01-0410-851d-a64ba20cac60
1222 lines
25 KiB
NASM
1222 lines
25 KiB
NASM
;;================================================================================================;;
|
|
;;//// png.asm //// (c) diamond, 2009 ////////////////////////////////////////////////////////////;;
|
|
;;================================================================================================;;
|
|
;; ;;
|
|
;; This file is part of Common development libraries (Libs-Dev). ;;
|
|
;; ;;
|
|
;; Libs-Dev is free software: you can redistribute it and/or modify it under the terms of the GNU ;;
|
|
;; Lesser General Public License as published by the Free Software Foundation, either version 2.1 ;;
|
|
;; of the License, or (at your option) any later version. ;;
|
|
;; ;;
|
|
;; Libs-Dev is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without ;;
|
|
;; even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;
|
|
;; Lesser General Public License for more details. ;;
|
|
;; ;;
|
|
;; You should have received a copy of the GNU Lesser General Public License along with Libs-Dev. ;;
|
|
;; If not, see <http://www.gnu.org/licenses/>. ;;
|
|
;; ;;
|
|
;;================================================================================================;;
|
|
|
|
include 'libpng/png.asm'
|
|
|
|
;;================================================================================================;;
|
|
;;proc img.is.png _data, _length ;////////////////////////////////////////////////////////////////;;
|
|
img.is.png:
|
|
;;------------------------------------------------------------------------------------------------;;
|
|
;? Determine if raw data could be decoded (is in PNG format) ;;
|
|
;;------------------------------------------------------------------------------------------------;;
|
|
;> _data = raw data as read from file/stream ;;
|
|
;> _length = data length ;;
|
|
;;------------------------------------------------------------------------------------------------;;
|
|
;< eax = false / true ;;
|
|
;;================================================================================================;;
|
|
; test 1 (length of data)
|
|
cmp dword [esp+8], 8
|
|
jb .nope
|
|
; test 2: signature
|
|
mov eax, [esp+4]
|
|
cmp dword [eax], 0x474E5089
|
|
jne .nope
|
|
cmp dword [eax+4], 0x0A1A0A0D
|
|
je .yep
|
|
|
|
.nope:
|
|
xor eax, eax
|
|
ret 8
|
|
|
|
.yep:
|
|
xor eax, eax
|
|
inc eax
|
|
ret 8
|
|
;endp
|
|
|
|
;;================================================================================================;;
|
|
;;proc img.decode.png _data, _length, _options ;//////////////////////////////////////////////////;;
|
|
img.decode.png:
|
|
xor eax, eax ; .image = 0
|
|
pushad
|
|
mov ebp, esp
|
|
.localsize = 30*4
|
|
virtual at ebp - .localsize
|
|
.width dd ?
|
|
.height dd ?
|
|
.bit_depth dd ?
|
|
.color_type dd ?
|
|
.transparent_color dd ?
|
|
.bytes_per_pixel dd ?
|
|
.scanline_len dd ?
|
|
.bits_per_pixel dd ?
|
|
.size_rest dd ?
|
|
.cur_chunk_ptr dd ?
|
|
.cur_chunk_size dd ?
|
|
.allocated dd ?
|
|
.paeth_a dd ?
|
|
.paeth_b dd ?
|
|
.paeth_c dd ?
|
|
.paeth_pa dd ?
|
|
.paeth_pb dd ?
|
|
.paeth_pc dd ?
|
|
.i dd ?
|
|
.j dd ?
|
|
; variables to handle interlace
|
|
.row_distance dd ? ; diff between two consecutives rows in destination
|
|
.col_distance dd ? ; summand for moving to next row in source
|
|
.row_increment dd ?
|
|
.col_increment dd ?
|
|
.block_height dd ?
|
|
.block_width dd ?
|
|
.interlace db ? ; 0 if not interlaced, 1 if interlaced
|
|
.row_increment_shift db ?
|
|
.col_increment_shift db ?
|
|
.shift db ? ; shift for current src byte
|
|
.starting_row dd ?
|
|
.starting_col dd ?
|
|
.idat_read dd ?
|
|
rb 1Ch
|
|
.image dd ?
|
|
rd 1
|
|
.data dd ?
|
|
.length dd ?
|
|
.options dd ?
|
|
end virtual
|
|
push eax ; .idat_read = 0
|
|
push eax ; .starting_col = 0
|
|
push eax ; .starting_row = 0
|
|
push eax ; .col_increment_shift, .row_increment_shift
|
|
inc eax
|
|
push eax ; .block_width
|
|
push eax ; .block_height
|
|
push eax ; .col_increment
|
|
push eax ; .row_increment
|
|
sub esp, .localsize-32
|
|
; load deflate unpacker, if not yet
|
|
; acquire mutex
|
|
@@:
|
|
push 1
|
|
pop eax
|
|
xchg [deflate_loader_mutex], eax ; 'xchg' has an implicit 'lock' prefix
|
|
test eax, eax
|
|
jz @f
|
|
mcall 5, 1
|
|
jmp @b
|
|
@@:
|
|
cmp [deflate_unpack2], __deflate_unpack2_import_name__
|
|
jnz .deflate_loaded
|
|
; do loading
|
|
invoke dll.load, @IMPORT
|
|
test eax, eax
|
|
jz .deflate_loaded
|
|
add esp, .localsize
|
|
popad
|
|
mov [deflate_loader_mutex], eax
|
|
ret 12
|
|
.deflate_loaded:
|
|
; release mutex
|
|
mov [deflate_loader_mutex], 0
|
|
; ok, continue
|
|
mov esi, [.data] ; esi -> data
|
|
mov ecx, [.length] ; ecx = length
|
|
; the signature has been already checked in img.is.png
|
|
lodsd
|
|
lodsd
|
|
sub ecx, 8
|
|
xor ebx, ebx ; no image allocated
|
|
.chunks_loop:
|
|
sub ecx, 12
|
|
jc .eof
|
|
lodsd ; chunk length
|
|
bswap eax
|
|
sub ecx, eax
|
|
jc .eof
|
|
push ecx ; save length of data rest
|
|
xchg eax, ecx ; ecx = size of data in the chunk
|
|
lodsd ; chunk type
|
|
cmp eax, 'IHDR'
|
|
jz .ihdr
|
|
cmp eax, 'IDAT'
|
|
jz .idat
|
|
cmp eax, 'IEND'
|
|
jz .iend
|
|
cmp eax, 'PLTE'
|
|
jz .palette
|
|
cmp eax, 'tRNS'
|
|
jz .transparency
|
|
; unrecognized chunk, ignore
|
|
lea esi, [esi+ecx+4]
|
|
pop ecx
|
|
jmp .chunks_loop
|
|
; IHDR chunk
|
|
.ihdr:
|
|
cmp ecx, 13
|
|
jnz .invalid_chunk
|
|
cmp [.image], 0
|
|
jnz .invalid_chunk
|
|
; read image characteristics
|
|
lodsd
|
|
bswap eax
|
|
mov [.width], eax
|
|
lodsd
|
|
bswap eax
|
|
mov [.height], eax
|
|
xor eax, eax
|
|
lea ebx, [eax+1]
|
|
lodsb
|
|
cmp al, 16
|
|
ja .invalid_chunk
|
|
test al, al
|
|
jz .invalid_chunk
|
|
lea edx, [eax-1]
|
|
test al, dl
|
|
jnz .invalid_chunk
|
|
mov [.bit_depth], eax
|
|
lodsb
|
|
test al, not 7
|
|
jnz .invalid_chunk
|
|
mov [.color_type], eax
|
|
lodsb
|
|
test al, al
|
|
jnz .invalid_chunk ; only compression method 0 is defined
|
|
lodsb
|
|
test al, al
|
|
jnz .invalid_chunk ; only filtering method 0 is defined
|
|
lodsb
|
|
cmp al, 1
|
|
ja .invalid_chunk ; only interlacing methods 0 and 1 are defined
|
|
mov [.interlace], al
|
|
; check for correctness and calculate bytes_per_pixel and scanline_len
|
|
mov eax, [.bit_depth]
|
|
mov edx, [.color_type]
|
|
dec edx
|
|
js .grayscale1
|
|
dec edx
|
|
jz .rgb1
|
|
dec edx
|
|
jz .palette1
|
|
dec edx
|
|
jz .grayscale_alpha1
|
|
dec edx
|
|
dec edx
|
|
jnz .invalid_chunk
|
|
.rgb_alpha1:
|
|
inc ebx
|
|
.rgb1:
|
|
inc ebx
|
|
.grayscale_alpha1:
|
|
inc ebx
|
|
cmp al, 8
|
|
jb .invalid_chunk
|
|
jmp @f
|
|
.palette1:
|
|
cmp al, 8
|
|
ja .invalid_chunk
|
|
.grayscale1:
|
|
@@:
|
|
mul ebx
|
|
mov [.bits_per_pixel], eax
|
|
add eax, 7
|
|
shr eax, 3
|
|
mov [.bytes_per_pixel], eax
|
|
mov [.transparent_color], 0
|
|
; allocate image
|
|
movi eax, Image.bpp32
|
|
cmp [.color_type], 2
|
|
jz @f
|
|
mov al, Image.bpp32
|
|
cmp [.color_type], 6
|
|
jz @f
|
|
mov al, Image.bpp8a
|
|
cmp [.color_type], 4
|
|
jz @f
|
|
mov al, Image.bpp8i
|
|
@@:
|
|
stdcall img.create, [.width], [.height], eax
|
|
test eax, eax
|
|
jz .invalid_chunk
|
|
mov [.image], eax
|
|
jmp .next_chunk
|
|
.invalid_chunk:
|
|
.iend:
|
|
pop ecx
|
|
.eof:
|
|
add esp, .localsize
|
|
popad
|
|
ret 12
|
|
; PLTE chunk
|
|
.palette:
|
|
mov eax, [.image]
|
|
test eax, eax
|
|
jz .invalid_chunk
|
|
cmp [.color_type], 3
|
|
jz .copy_palette
|
|
.ignore_chunk:
|
|
add esi, ecx
|
|
.next_chunk:
|
|
lodsd
|
|
pop ecx
|
|
jmp .chunks_loop
|
|
.copy_palette:
|
|
mov edi, [eax + Image.Palette]
|
|
xor eax, eax
|
|
cmp ecx, 256*3
|
|
ja .next_chunk
|
|
@@:
|
|
sub ecx, 3
|
|
jz @f
|
|
js .invalid_chunk
|
|
lodsd
|
|
dec esi
|
|
bswap eax
|
|
mov al, 0xff
|
|
ror eax, 8
|
|
stosd
|
|
jmp @b
|
|
@@:
|
|
lodsd
|
|
dec esi
|
|
bswap eax
|
|
mov al, 0xff
|
|
ror eax, 8
|
|
stosd
|
|
jmp .next_chunk
|
|
; convert 16 bit sample to 8 bit sample
|
|
macro convert_16_to_8
|
|
{
|
|
local .l1,.l2
|
|
xor ah, 0x80
|
|
js .l1
|
|
cmp al, ah
|
|
adc al, 0
|
|
jmp .l2
|
|
.l1:
|
|
cmp ah, al
|
|
sbb al, 0
|
|
.l2:
|
|
}
|
|
; tRNS chunk
|
|
.transparency:
|
|
mov eax, [.image]
|
|
test eax, eax
|
|
jz .invalid_chunk
|
|
cmp [.color_type], 2 ; rgb
|
|
jz .transparency_rgb
|
|
cmp [.color_type], 3 ; indexed
|
|
jz .transparency_indexed
|
|
add esi, ecx
|
|
jmp .next_chunk
|
|
.transparency_rgb:
|
|
cmp [.bit_depth], 16
|
|
jz .transparency_rgb16
|
|
lodsw
|
|
shl eax, 8
|
|
lodsw
|
|
shl eax, 8
|
|
lodsw
|
|
mov al, 0xff
|
|
ror eax, 8
|
|
mov [.transparent_color], eax
|
|
jmp .next_chunk
|
|
.transparency_rgb16:
|
|
lodsw
|
|
convert_16_to_8
|
|
shl ax, 8
|
|
shl eax, 8
|
|
lodsw
|
|
convert_16_to_8
|
|
shl ax, 8
|
|
shl eax, 8
|
|
lodsw
|
|
convert_16_to_8
|
|
shl ax, 8
|
|
mov al, 0xff
|
|
ror eax, 8
|
|
mov [.transparent_color], eax
|
|
jmp .next_chunk
|
|
.transparency_indexed:
|
|
mov edi, [eax + Image.Palette]
|
|
@@:
|
|
add edi, 3
|
|
movsb
|
|
loop @b
|
|
jmp .next_chunk
|
|
.idat:
|
|
test ecx, ecx
|
|
jz .next_chunk
|
|
cmp [.idat_read], 0
|
|
jnz @f
|
|
lodsb
|
|
inc [.idat_read]
|
|
and al, 0xF
|
|
cmp al, 8
|
|
jnz .invalid_chunk
|
|
dec ecx
|
|
jz .next_chunk
|
|
@@:
|
|
cmp [.idat_read], 1
|
|
jnz @f
|
|
lodsb
|
|
inc [.idat_read]
|
|
test al, 20h
|
|
jnz .invalid_chunk
|
|
dec ecx
|
|
jz .next_chunk
|
|
@@:
|
|
mov [.cur_chunk_ptr], esi
|
|
mov [.cur_chunk_size], ecx
|
|
pop [.length]
|
|
push eax
|
|
push esp
|
|
push ebp
|
|
push .deflate_callback
|
|
call [deflate_unpack2]
|
|
pop ecx
|
|
test eax, eax
|
|
jz .invalid_chunk
|
|
; convert PNG unpacked data to RAW data
|
|
mov esi, eax
|
|
mov [.allocated], eax
|
|
mov [.size_rest], ecx
|
|
; unfilter and deinterlace
|
|
; .interlace_pass, .starting_row and .starting_col have been already set to 0
|
|
; .block_width, .block_height, .col_increment, .row_increment were set
|
|
; to values for non-interlaced images; correct if necessary
|
|
cmp [.interlace], 0
|
|
jz .deinterlace_loop
|
|
push 8
|
|
pop eax
|
|
mov [.row_increment], eax
|
|
mov [.col_increment], eax
|
|
mov [.block_height], eax
|
|
mov [.block_width], eax
|
|
mov [.row_increment_shift], 3
|
|
mov [.col_increment_shift], 3
|
|
.deinterlace_loop:
|
|
mov edx, [.height]
|
|
cmp edx, [.starting_row]
|
|
jbe .deinterlace_next
|
|
mov ebx, [.width]
|
|
sub ebx, [.starting_col]
|
|
jbe .deinterlace_next
|
|
mov cl, [.col_increment_shift]
|
|
add ebx, [.col_increment]
|
|
dec ebx
|
|
shr ebx, cl
|
|
mov eax, [.bits_per_pixel]
|
|
imul eax, ebx
|
|
add eax, 7
|
|
shr eax, 3
|
|
mov [.scanline_len], eax
|
|
shl ebx, cl
|
|
mov [.col_distance], ebx
|
|
; Unfilter
|
|
mov ecx, [.size_rest]
|
|
push esi
|
|
.unfilter_loop_e:
|
|
mov ebx, [.scanline_len]
|
|
sub ecx, 1
|
|
jc .unfilter_abort
|
|
sub ecx, ebx
|
|
jc .unfilter_abort
|
|
movzx eax, byte [esi]
|
|
add esi, 1
|
|
cmp eax, 4
|
|
ja .next_scanline
|
|
jmp dword [@f + eax*4]
|
|
align 4
|
|
@@:
|
|
dd .unfilter_none
|
|
dd .unfilter_sub
|
|
dd .unfilter_up
|
|
dd .unfilter_average
|
|
dd .unfilter_paeth
|
|
.unfilter_sub:
|
|
mov edi, [.bytes_per_pixel]
|
|
add esi, edi
|
|
sub ebx, edi
|
|
jbe .next_scanline
|
|
neg edi
|
|
@@:
|
|
mov al, [esi+edi]
|
|
add [esi], al
|
|
add esi, 1
|
|
sub ebx, 1
|
|
jnz @b
|
|
jmp .next_scanline
|
|
.unfilter_up:
|
|
cmp edx, [.height]
|
|
jz .unfilter_none
|
|
lea edi, [ebx+1]
|
|
neg edi
|
|
@@:
|
|
mov al, [esi+edi]
|
|
add [esi], al
|
|
add esi, 1
|
|
sub ebx, 1
|
|
jnz @b
|
|
jmp .next_scanline
|
|
.unfilter_average:
|
|
mov edi, [.bytes_per_pixel]
|
|
cmp edx, [.height]
|
|
jz .unfilter_average_firstline
|
|
push edx
|
|
lea edx, [ebx+1]
|
|
neg edx
|
|
sub ebx, edi
|
|
@@:
|
|
mov al, [esi+edx]
|
|
shr al, 1
|
|
add [esi], al
|
|
add esi, 1
|
|
sub edi, 1
|
|
jnz @b
|
|
mov edi, [.bytes_per_pixel]
|
|
neg edi
|
|
test ebx, ebx
|
|
jz .unfilter_average_done
|
|
@@:
|
|
mov al, [esi+edx]
|
|
add al, [esi+edi]
|
|
rcr al, 1
|
|
add [esi], al
|
|
add esi, 1
|
|
sub ebx, 1
|
|
jnz @b
|
|
.unfilter_average_done:
|
|
pop edx
|
|
jmp .next_scanline
|
|
.unfilter_average_firstline:
|
|
mov edi, [.bytes_per_pixel]
|
|
add esi, edi
|
|
sub ebx, edi
|
|
jbe .next_scanline
|
|
neg edi
|
|
@@:
|
|
mov al, [esi+edi]
|
|
shr al, 1
|
|
add [esi], al
|
|
add esi, 1
|
|
sub ebx, 1
|
|
jnz @b
|
|
jmp .unfilter_none
|
|
.unfilter_paeth:
|
|
cmp edx, [.height]
|
|
jz .unfilter_sub
|
|
push edx
|
|
lea edx, [ebx+1]
|
|
mov edi, [.bytes_per_pixel]
|
|
neg edx
|
|
sub ebx, edi
|
|
@@:
|
|
mov al, [esi+edx]
|
|
add [esi], al
|
|
add esi, 1
|
|
sub edi, 1
|
|
jnz @b
|
|
mov edi, [.bytes_per_pixel]
|
|
neg edi
|
|
test ebx, ebx
|
|
jz .unfilter_paeth_done
|
|
push ecx
|
|
@@:
|
|
push ebx
|
|
; PaethPredictor(Raw(x-bpp) = a, Prior(x) = b, Prior(x-bpp) = c)
|
|
movzx eax, byte [esi+edi]
|
|
mov [.paeth_a], eax
|
|
movzx ecx, byte [esi+edx]
|
|
add edi, edx
|
|
mov [.paeth_b], ecx
|
|
add ecx, eax
|
|
movzx eax, byte [esi+edi]
|
|
mov [.paeth_c], eax
|
|
sub ecx, eax ; ecx = a + b - c = p
|
|
; calculate pa = abs(p-a), pb = abs(p-b), pc = abs(p-c)
|
|
mov ebx, ecx
|
|
sub ebx, eax ; ebx = p - c
|
|
cmp ebx, 80000000h
|
|
sbb eax, eax ; eax = (p < c) ? 0 : 0xFFFFFFF
|
|
not eax ; eax = (p < c) ? 0xFFFFFFFF : 0
|
|
and eax, ebx ; eax = (p < c) ? p - c : 0
|
|
sub ebx, eax
|
|
sub ebx, eax ; ebx = abs(p-c)
|
|
mov [.paeth_pc], ebx
|
|
mov ebx, ecx
|
|
sub ebx, [.paeth_a]
|
|
cmp ebx, 80000000h
|
|
sbb eax, eax
|
|
not eax
|
|
and eax, ebx
|
|
sub ebx, eax
|
|
sub ebx, eax
|
|
mov [.paeth_pa], ebx
|
|
mov ebx, ecx
|
|
sub ebx, [.paeth_b]
|
|
cmp ebx, 80000000h
|
|
sbb eax, eax
|
|
not eax
|
|
and eax, ebx
|
|
sub ebx, eax
|
|
sub ebx, eax
|
|
;mov [.paeth_pb], ebx
|
|
; select closest value
|
|
push edx
|
|
mov edx, [.paeth_b]
|
|
sub edx, [.paeth_a]
|
|
sub ebx, [.paeth_pa]
|
|
sbb ecx, ecx ; ecx = (pa > pb) ? 0xFFFFFFFF : 0
|
|
sbb eax, eax ; eax = (pa > pb) ? 0xFFFFFFFF : 0
|
|
and ecx, ebx ; ecx = (pa > pb) ? pb - pa : 0
|
|
and eax, edx ; eax = (pa > pb) ? b - a : 0
|
|
add ecx, [.paeth_pa] ; ecx = (pa > pb) ? pb : pa = min(pa,pb)
|
|
add eax, [.paeth_a] ; eax = (pa > pb) ? b : a
|
|
mov edx, [.paeth_c]
|
|
sub edx, eax
|
|
sub [.paeth_pc], ecx
|
|
sbb ebx, ebx ; ebx = (min(pa,pb) <= pc) ? 0 : 0xFFFFFFFF
|
|
and ebx, edx ; ebx = (min(pa,pb) <= pc) ? 0 : c - eax
|
|
add eax, ebx
|
|
pop edx
|
|
add [esi], al
|
|
pop ebx
|
|
sub edi, edx
|
|
add esi, 1
|
|
sub ebx, 1
|
|
jnz @b
|
|
pop ecx
|
|
.unfilter_paeth_done:
|
|
pop edx
|
|
jmp .next_scanline
|
|
.unfilter_none:
|
|
add esi, ebx
|
|
.next_scanline:
|
|
sub edx, [.row_increment]
|
|
jc .unfilter_done
|
|
cmp edx, [.starting_row]
|
|
jbe .unfilter_done
|
|
jmp .unfilter_loop_e
|
|
.unfilter_abort:
|
|
xor ecx, ecx
|
|
.unfilter_done:
|
|
; unfiltering done, now convert to raw data
|
|
; with deinterlacing if needed
|
|
pop esi
|
|
mov ebx, [.image]
|
|
mov eax, [.width]
|
|
call img._.get_scanline_len
|
|
mov [.row_distance], eax
|
|
mov eax, [.row_increment]
|
|
mul [.width]
|
|
sub eax, [.col_distance]
|
|
call img._.get_scanline_len
|
|
mov [.col_distance], eax
|
|
mov edi, [ebx + Image.Data]
|
|
mov eax, [.starting_row]
|
|
mul [.width]
|
|
add eax, [.starting_col]
|
|
call img._.get_scanline_len
|
|
add edi, eax
|
|
mov eax, ebx
|
|
mov ebx, [.size_rest]
|
|
mov [.size_rest], ecx
|
|
mov edx, [.height]
|
|
sub edx, [.starting_row]
|
|
mov [.j], edx
|
|
cmp [.color_type], 0
|
|
jz .grayscale2
|
|
cmp [.color_type], 2
|
|
jz .rgb2
|
|
cmp [.color_type], 3
|
|
jz .palette2
|
|
cmp [.color_type], 4
|
|
jz .grayscale_alpha2
|
|
.rgb_alpha2:
|
|
cmp [.bit_depth], 16
|
|
jz .rgb_alpha2_16bit
|
|
.rgb_alpha2.next:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
.rgb_alpha2.extloop:
|
|
|
|
macro init_block
|
|
{
|
|
push ebx
|
|
mov eax, [.col_increment]
|
|
mov edx, [.j]
|
|
cmp edx, [.block_height]
|
|
jb @f
|
|
mov edx, [.block_height]
|
|
@@:
|
|
mov ebx, [.i]
|
|
cmp ebx, [.block_width]
|
|
jb @f
|
|
mov ebx, [.block_width]
|
|
@@:
|
|
}
|
|
|
|
init_block
|
|
lea eax, [edi+eax*4]
|
|
push eax
|
|
.rgb_alpha2.innloop1:
|
|
push edi
|
|
mov ecx, ebx
|
|
.rgb_alpha2.innloop2:
|
|
mov al, [esi+2]
|
|
mov [edi], al
|
|
mov al, [esi+1]
|
|
mov [edi+1], al
|
|
mov al, [esi]
|
|
mov [edi+2], al
|
|
mov al, [esi+3]
|
|
mov [edi+3], al
|
|
add edi, 4
|
|
dec ecx
|
|
jnz .rgb_alpha2.innloop2
|
|
pop edi
|
|
add edi, [.row_distance]
|
|
dec edx
|
|
jnz .rgb_alpha2.innloop1
|
|
pop edi ebx
|
|
add esi, 4
|
|
mov eax, [.col_increment]
|
|
sub [.i], eax
|
|
ja .rgb_alpha2.extloop
|
|
add edi, [.col_distance]
|
|
mov eax, [.row_increment]
|
|
sub [.j], eax
|
|
ja .rgb_alpha2.next
|
|
jmp .convert_done
|
|
.rgb_alpha2_16bit:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
.rgb_alpha2_16bit.loop:
|
|
init_block
|
|
lea eax, [edi+eax*4]
|
|
push eax
|
|
.rgb_alpha2_16bit.innloop1:
|
|
push edi
|
|
mov ecx, ebx
|
|
.rgb_alpha2_16bit.innloop2:
|
|
mov ax, [esi+4]
|
|
convert_16_to_8
|
|
mov [edi], al
|
|
mov ax, [esi+2]
|
|
convert_16_to_8
|
|
mov [edi+1], al
|
|
mov ax, [esi]
|
|
convert_16_to_8
|
|
mov [edi+2], al
|
|
;mov ax, [esi+6]
|
|
;convert_16_to_8
|
|
;mov [edi+3], al
|
|
add edi, 4
|
|
dec ecx
|
|
jnz .rgb_alpha2_16bit.innloop2
|
|
pop edi
|
|
add edi, [.row_distance]
|
|
dec edx
|
|
jnz .rgb_alpha2_16bit.innloop1
|
|
pop edi ebx
|
|
add esi, 8
|
|
mov eax, [.col_increment]
|
|
sub [.i], eax
|
|
ja .rgb_alpha2_16bit.loop
|
|
add edi, [.col_distance]
|
|
mov eax, [.row_increment]
|
|
sub [.j], eax
|
|
ja .rgb_alpha2_16bit
|
|
jmp .convert_done
|
|
.grayscale2:
|
|
call .create_grayscale_palette
|
|
cmp [.bit_depth], 16
|
|
jz .grayscale2_16bit
|
|
.palette2:
|
|
cmp [.bit_depth], 1
|
|
jz .palette2_1bit
|
|
cmp [.bit_depth], 2
|
|
jz .palette2_2bit
|
|
cmp [.bit_depth], 4
|
|
jz .palette2_4bit
|
|
.palette2_8bit:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
.palette2_8bit.extloop:
|
|
init_block
|
|
add eax, edi
|
|
push eax
|
|
mov al, [esi]
|
|
inc esi
|
|
macro block_byte_innerloop extloop
|
|
{
|
|
local .l1
|
|
.l1:
|
|
mov ecx, ebx
|
|
rep stosb
|
|
sub edi, ebx
|
|
add edi, [.row_distance]
|
|
dec edx
|
|
jnz .l1
|
|
pop edi ebx
|
|
mov eax, [.col_increment]
|
|
sub [.i], eax
|
|
ja extloop
|
|
add edi, [.col_distance]
|
|
mov eax, [.row_increment]
|
|
sub [.j], eax
|
|
}
|
|
block_byte_innerloop .palette2_8bit.extloop
|
|
ja .palette2_8bit
|
|
jmp .convert_done
|
|
.palette2_4bit:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
mov [.shift], 0
|
|
.palette2_4bit.extloop:
|
|
init_block
|
|
add eax, edi
|
|
push eax
|
|
xor [.shift], 1
|
|
jz .palette2_4bit.shifted
|
|
mov al, [esi]
|
|
inc esi
|
|
shr al, 4
|
|
jmp @f
|
|
.palette2_4bit.shifted:
|
|
mov al, [esi-1]
|
|
and al, 0xF
|
|
@@:
|
|
block_byte_innerloop .palette2_4bit.extloop
|
|
ja .palette2_4bit
|
|
jmp .convert_done
|
|
.palette2_2bit:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
mov [.shift], 0
|
|
.palette2_2bit.extloop:
|
|
init_block
|
|
add eax, edi
|
|
push eax
|
|
mov cl, [.shift]
|
|
sub cl, 2
|
|
jns .palette2_2bit.shifted
|
|
mov cl, 6
|
|
mov al, [esi]
|
|
inc esi
|
|
shr al, cl
|
|
jmp @f
|
|
.palette2_2bit.shifted:
|
|
mov al, [esi-1]
|
|
shr al, cl
|
|
and al, 3
|
|
@@:
|
|
mov [.shift], cl
|
|
block_byte_innerloop .palette2_2bit.extloop
|
|
ja .palette2_2bit
|
|
jmp .convert_done
|
|
.palette2_1bit:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
mov [.shift], 0
|
|
.palette2_1bit.extloop:
|
|
init_block
|
|
add eax, edi
|
|
push eax
|
|
mov cl, [.shift]
|
|
dec cl
|
|
jns .palette2_1bit.shifted
|
|
mov cl, 7
|
|
mov al, [esi]
|
|
inc esi
|
|
shr al, cl
|
|
jmp @f
|
|
.palette2_1bit.shifted:
|
|
mov al, [esi-1]
|
|
shr al, cl
|
|
and al, 1
|
|
@@:
|
|
mov [.shift], cl
|
|
block_byte_innerloop .palette2_1bit.extloop
|
|
ja .palette2_1bit
|
|
jmp .convert_done
|
|
.grayscale2_16bit:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
.grayscale2_16bit.extloop:
|
|
init_block
|
|
add eax, edi
|
|
push eax
|
|
mov ax, [esi]
|
|
add esi, 2
|
|
convert_16_to_8
|
|
block_byte_innerloop .grayscale2_16bit.extloop
|
|
ja .grayscale2_16bit
|
|
jmp .convert_done
|
|
.rgb2:
|
|
cmp [.bit_depth], 16
|
|
jz .rgb2_16bit
|
|
.rgb2.next:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
.rgb2.extloop:
|
|
init_block
|
|
lea eax, [edi+eax*4]
|
|
push eax
|
|
.rgb2.innloop1:
|
|
push edi
|
|
mov ecx, ebx
|
|
.rgb2.innloop2:
|
|
mov eax, [esi]
|
|
bswap eax
|
|
mov al, 0xff
|
|
ror eax, 8
|
|
cmp eax, [.transparent_color]
|
|
jnz @f
|
|
and eax, 0x00ffffff
|
|
@@:
|
|
stosd
|
|
dec ecx
|
|
jnz .rgb2.innloop2
|
|
pop edi
|
|
add edi, [.row_distance]
|
|
dec edx
|
|
jnz .rgb2.innloop1
|
|
pop edi ebx
|
|
add esi, 3
|
|
mov eax, [.col_increment]
|
|
sub [.i], eax
|
|
ja .rgb2.extloop
|
|
add edi, [.col_distance]
|
|
mov eax, [.row_increment]
|
|
sub [.j], eax
|
|
ja .rgb2.next
|
|
jmp .convert_done
|
|
.rgb2_16bit:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
.rgb2_16bit.extloop:
|
|
init_block
|
|
lea eax, [eax*3]
|
|
add eax, edi
|
|
push eax
|
|
.rgb2_16bit.innloop1:
|
|
push edi
|
|
mov ecx, ebx
|
|
.rgb2_16bit.innloop2:
|
|
mov ax, [esi+4]
|
|
convert_16_to_8
|
|
mov [edi], al
|
|
mov ax, [esi+2]
|
|
convert_16_to_8
|
|
mov [edi+1], al
|
|
mov ax, [esi]
|
|
convert_16_to_8
|
|
mov [edi+2], al
|
|
add edi, 3
|
|
dec ecx
|
|
jnz .rgb2_16bit.innloop2
|
|
pop edi
|
|
add edi, [.row_distance]
|
|
dec edx
|
|
jnz .rgb2_16bit.innloop1
|
|
pop edi ebx
|
|
add esi, 6
|
|
mov eax, [.col_increment]
|
|
sub [.i], eax
|
|
ja .rgb2_16bit.extloop
|
|
add edi, [.col_distance]
|
|
mov eax, [.row_increment]
|
|
sub [.j], eax
|
|
ja .rgb2_16bit
|
|
jmp .convert_done
|
|
.grayscale_alpha2:
|
|
cmp [.bit_depth], 16
|
|
jz .grayscale_alpha2_16bit
|
|
.grayscale_alpha2.next:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
.grayscale_alpha2.extloop:
|
|
init_block
|
|
add eax, eax
|
|
add eax, edi
|
|
push eax
|
|
lodsw
|
|
macro block_word_innerloop extloop
|
|
{
|
|
local .l1
|
|
.l1:
|
|
mov ecx, ebx
|
|
rep stosw
|
|
sub edi, ebx
|
|
add edi, [.row_distance]
|
|
dec edx
|
|
jnz .l1
|
|
pop edi ebx
|
|
mov eax, [.col_increment]
|
|
sub [.i], eax
|
|
ja extloop
|
|
add edi, [.col_distance]
|
|
mov eax, [.row_increment]
|
|
sub [.j], eax
|
|
}
|
|
block_word_innerloop .grayscale_alpha2.extloop
|
|
ja .grayscale_alpha2.next
|
|
jmp .convert_done
|
|
.grayscale_alpha2_16bit:
|
|
sub ebx, 1
|
|
jc .convert_done
|
|
add esi, 1
|
|
sub ebx, [.scanline_len]
|
|
jc .convert_done
|
|
mov ecx, [.width]
|
|
sub ecx, [.starting_col]
|
|
mov [.i], ecx
|
|
.grayscale_alpha2_16bit.extloop:
|
|
init_block
|
|
add eax, eax
|
|
add eax, edi
|
|
push eax
|
|
lodsw
|
|
convert_16_to_8
|
|
mov ecx, eax
|
|
lodsw
|
|
convert_16_to_8
|
|
mov ah, cl
|
|
xchg al, ah
|
|
block_word_innerloop .grayscale_alpha2_16bit.extloop
|
|
ja .grayscale_alpha2_16bit
|
|
.convert_done:
|
|
; next interlace pass
|
|
.deinterlace_next:
|
|
mov eax, [.block_width]
|
|
cmp eax, [.block_height]
|
|
jz .deinterlace_dec_width
|
|
mov [.block_height], eax
|
|
mov [.col_increment], eax
|
|
dec [.col_increment_shift]
|
|
mov [.starting_row], eax
|
|
and [.starting_col], 0
|
|
jmp .deinterlace_loop
|
|
.deinterlace_dec_width:
|
|
shr eax, 1
|
|
jz .deinterlace_done
|
|
mov [.block_width], eax
|
|
mov [.starting_col], eax
|
|
add eax, eax
|
|
and [.starting_row], 0
|
|
mov [.row_increment], eax
|
|
bsf eax, eax
|
|
mov [.row_increment_shift], al
|
|
jmp .deinterlace_loop
|
|
.deinterlace_done:
|
|
mcall 68, 13, [.allocated]
|
|
mov esi, [.cur_chunk_ptr]
|
|
add esi, [.cur_chunk_size]
|
|
push [.length]
|
|
jmp .next_chunk
|
|
|
|
.deflate_callback:
|
|
mov ebp, [esp+4]
|
|
mov ebx, [esp+8]
|
|
xor eax, eax
|
|
mov esi, [.cur_chunk_size]
|
|
mov [ebx], esi
|
|
test esi, esi
|
|
jz .deflate_callback.ret
|
|
mov eax, [.cur_chunk_ptr]
|
|
mov ecx, [.length]
|
|
add esi, eax
|
|
mov [.cur_chunk_ptr], esi
|
|
and [.cur_chunk_size], 0
|
|
@@:
|
|
sub ecx, 12
|
|
jb .deflate_callback.ret
|
|
cmp dword [esi+4+4], 'IDAT'
|
|
jnz .deflate_callback.ret
|
|
mov edx, [esi+4]
|
|
bswap edx
|
|
sub ecx, edx
|
|
jb .deflate_callback.ret
|
|
add esi, 4+8
|
|
test edx, edx
|
|
jz @b
|
|
mov [.cur_chunk_size], edx
|
|
mov [.cur_chunk_ptr], esi
|
|
mov [.length], ecx
|
|
.deflate_callback.ret:
|
|
ret 8
|
|
|
|
.create_grayscale_palette:
|
|
push edi edx
|
|
mov edi, [eax + Image.Palette]
|
|
mov ecx, [.bit_depth]
|
|
cmp cl, 16
|
|
jnz @f
|
|
mov cl, 8
|
|
@@:
|
|
push 1
|
|
pop eax
|
|
shl eax, cl
|
|
xchg eax, ecx
|
|
mov edx, 0x010101
|
|
cmp al, 8
|
|
jz .graypal_common
|
|
mov edx, 0x111111
|
|
cmp al, 4
|
|
jz .graypal_common
|
|
mov edx, 0x555555
|
|
cmp al, 2
|
|
jz .graypal_common
|
|
mov edx, 0xFFFFFF
|
|
.graypal_common:
|
|
mov eax, 0xff000000
|
|
@@:
|
|
stosd
|
|
add eax, edx
|
|
loop @b
|
|
pop edx edi
|
|
ret
|
|
;endp
|
|
|
|
|
|
|
|
;;================================================================================================;;
|
|
align 4
|
|
proc img.encode.png uses ebx edx, _img:dword, _common:dword, _specific:dword
|
|
;;------------------------------------------------------------------------------------------------;;
|
|
;? Encode image into raw data in png format ;;
|
|
;;------------------------------------------------------------------------------------------------;;
|
|
;> [_img] = pointer to image ;;
|
|
;> [_common] = format independent options ;;
|
|
;> [_specific] = 0 / pointer to the structure of format specific options ;;
|
|
;;------------------------------------------------------------------------------------------------;;
|
|
;< eax = 0 / pointer to encoded data ;;
|
|
;< ecx = error code / the size of encoded data ;;
|
|
;;================================================================================================;;
|
|
locals
|
|
encoded_file rd 1
|
|
encoded_file_size rd 1
|
|
simag png_image
|
|
endl
|
|
mov ebx,[_img]
|
|
mov eax,[ebx+Image.Type]
|
|
cmp eax,Image.bpp24
|
|
je @f
|
|
mov ecx,LIBIMG_ERROR_BIT_DEPTH
|
|
jmp .error
|
|
@@:
|
|
|
|
mov edx,ebp
|
|
sub edx,sizeof.png_image
|
|
mov dword[edx+png_image.version],PNG_IMAGE_VERSION
|
|
mov ecx,[ebx+Image.Width]
|
|
mov [edx+png_image.width],ecx ;Image width in pixels (columns)
|
|
mov eax,[ebx+Image.Height]
|
|
mov [edx+png_image.height],eax ;Image height in pixels (rows)
|
|
mov dword[edx+png_image.format],PNG_COLOR_TYPE_RGB
|
|
;mov dword[edx+png_image.flags],PNG_IMAGE_FLAG_???
|
|
|
|
imul ecx,3
|
|
mov edi,ecx
|
|
imul edi,[ebx+Image.Height]
|
|
cmp edi,4096
|
|
jge @f
|
|
mov edi,4096 ;minimum memory size
|
|
@@:
|
|
mov [encoded_file_size],edi
|
|
stdcall [mem.alloc],edi
|
|
test eax,eax
|
|
jnz @f
|
|
mov ecx,LIBIMG_ERROR_OUT_OF_MEMORY
|
|
jmp .error
|
|
@@:
|
|
mov [encoded_file],eax
|
|
mov edi,edx
|
|
sub edi,4
|
|
stdcall png_image_write_to_memory, edx,eax,edi,0,[ebx+Image.Data],ecx,0
|
|
mov eax,[encoded_file]
|
|
mov ecx,[encoded_file_size]
|
|
jmp .quit
|
|
|
|
.error:
|
|
xor eax,eax
|
|
.quit:
|
|
ret
|
|
endp
|