forked from KolibriOS/kolibrios
ff3cee003b
git-svn-id: svn://kolibrios.org@6881 a494cfbc-eb01-0410-851d-a64ba20cac60
3309 lines
88 KiB
NASM
3309 lines
88 KiB
NASM
|
|
; pngwutil.asm - utilities to write a PNG file
|
|
|
|
; Last changed in libpng 1.6.24 [August 4, 2016]
|
|
; Copyright (c) 1998-2002,2004,2006-2016 Glenn Randers-Pehrson
|
|
; (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
|
|
; (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
|
|
|
|
; This code is released under the libpng license.
|
|
; For conditions of distribution and use, see the disclaimer
|
|
; and license in png.inc
|
|
|
|
; Place a 32-bit number into a buffer in PNG byte order. We work
|
|
; with unsigned numbers for convenience, although one supported
|
|
; ancillary chunk uses signed (two's complement) numbers.
|
|
|
|
;void (bytep buf, uint_32 i)
|
|
align 4
|
|
proc png_save_uint_32 uses eax edi, buf:dword, i:dword
|
|
mov eax,[i]
|
|
bswap eax
|
|
mov edi,[buf]
|
|
stosd
|
|
ret
|
|
endp
|
|
|
|
; Place a 16-bit number into a buffer in PNG byte order.
|
|
; The parameter is declared unsigned int, not uint_16,
|
|
; just to avoid potential problems on pre-ANSI C compilers.
|
|
|
|
;void (bytep buf, unsigned int i)
|
|
align 4
|
|
proc png_save_uint_16 uses eax edi, buf:dword, i:dword
|
|
mov eax,[i]
|
|
ror eax,16
|
|
bswap eax
|
|
mov edi,[buf]
|
|
stosw
|
|
ret
|
|
endp
|
|
|
|
; Simple function to write the signature. If we have already written
|
|
; the magic bytes of the signature, or more likely, the PNG stream is
|
|
; being embedded into another stream and doesn't need its own signature,
|
|
; we should call png_set_sig_bytes() to tell libpng how many of the
|
|
; bytes have already been written.
|
|
|
|
align 4
|
|
png_signature db 137, 80, 78, 71, 13, 10, 26, 10
|
|
|
|
;void (png_structrp png_ptr)
|
|
align 4
|
|
proc png_write_sig uses eax ebx edi, png_ptr:dword
|
|
if PNG_IO_STATE_SUPPORTED eq 1
|
|
; Inform the I/O callback that the signature is being written
|
|
mov edi,[png_ptr]
|
|
mov dword[edi+png_struct.io_state], PNG_IO_WRITING or PNG_IO_SIGNATURE
|
|
end if
|
|
|
|
; Write the rest of the 8 byte signature
|
|
movzx eax,byte[edi+png_struct.sig_bytes]
|
|
mov ebx,8
|
|
sub ebx,eax
|
|
add eax,png_signature
|
|
stdcall png_write_data, edi, eax, ebx
|
|
|
|
cmp byte[edi+png_struct.sig_bytes], 3 ;if(..<3)
|
|
jge @f
|
|
or dword[edi+png_struct.mode], PNG_HAVE_PNG_SIGNATURE
|
|
@@:
|
|
ret
|
|
endp
|
|
|
|
; Write the start of a PNG chunk. The type is the chunk type.
|
|
; The total_length is the sum of the lengths of all the data you will be
|
|
; passing in png_write_chunk_data().
|
|
|
|
;void (png_structrp png_ptr, uint_32 chunk_name, uint_32 length)
|
|
align 4
|
|
proc png_write_chunk_header uses ebx edi, png_ptr:dword, chunk_name:dword, length:dword
|
|
locals
|
|
buf rb 8 ;ebp-8
|
|
endl
|
|
|
|
if (PNG_DEBUG eq 1) & (PNG_DEBUG > 0)
|
|
; PNG_CSTRING_FROM_CHUNK(buf, chunk_name);
|
|
; png_debug2(0, "Writing %s chunk, length = %lu", buf, (unsigned long)length);
|
|
end if
|
|
|
|
mov edi,[png_ptr]
|
|
test edi,edi
|
|
jz .end_f ;if (..==0) return
|
|
|
|
if PNG_IO_STATE_SUPPORTED eq 1
|
|
; Inform the I/O callback that the chunk header is being written.
|
|
; PNG_IO_CHUNK_HDR requires a single I/O call.
|
|
|
|
mov dword[edi+png_struct.io_state], PNG_IO_WRITING or PNG_IO_CHUNK_HDR
|
|
end if
|
|
|
|
; Write the length and the chunk name
|
|
lea ebx,[ebp-8]
|
|
stdcall png_save_uint_32, ebx, [length]
|
|
m2m dword[ebx+4],dword[chunk_name]
|
|
stdcall png_write_data, edi, ebx, 8
|
|
|
|
; Put the chunk name into png_ptr->chunk_name
|
|
m2m dword[edi+png_struct.chunk_name],dword[chunk_name]
|
|
|
|
; Reset the crc and run it over the chunk name
|
|
stdcall png_reset_crc, edi
|
|
lea ebx,[ebp-4] ;buf + 4
|
|
stdcall png_calculate_crc, edi, ebx, 4
|
|
|
|
if PNG_IO_STATE_SUPPORTED eq 1
|
|
; Inform the I/O callback that chunk data will (possibly) be written.
|
|
; PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls.
|
|
|
|
mov dword[edi+png_struct.io_state], PNG_IO_WRITING or PNG_IO_CHUNK_DATA
|
|
end if
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
;void (png_structrp png_ptr, bytep chunk_string, uint_32 length)
|
|
align 4
|
|
proc png_write_chunk_start uses eax, png_ptr:dword, chunk_string:dword, length:dword
|
|
mov eax,[chunk_string]
|
|
stdcall png_write_chunk_header, [png_ptr], [eax], [length]
|
|
ret
|
|
endp
|
|
|
|
; Write the data of a PNG chunk started with png_write_chunk_header().
|
|
; Note that multiple calls to this function are allowed, and that the
|
|
; sum of the lengths from these calls *must* add up to the total_length
|
|
; given to png_write_chunk_header().
|
|
|
|
;void (png_structrp png_ptr, bytep data, png_size_t length)
|
|
align 4
|
|
proc png_write_chunk_data uses edi, png_ptr:dword, p2data:dword, length:dword
|
|
; Write the data, and run the CRC over it
|
|
mov edi,[png_ptr]
|
|
cmp edi,0
|
|
je .end_f ;if (..==0) return
|
|
|
|
cmp dword[p2data],0
|
|
je .end_f
|
|
cmp dword[length],0
|
|
jle .end_f ;if (..!=0 && ..>0)
|
|
stdcall png_write_data, edi, [p2data], [length]
|
|
; Update the CRC after writing the data,
|
|
; in case the user I/O routine alters it.
|
|
stdcall png_calculate_crc, edi, [p2data], [length]
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; Finish a chunk started with png_write_chunk_header().
|
|
;void (png_structrp png_ptr)
|
|
align 4
|
|
proc png_write_chunk_end uses ebx edi, png_ptr:dword
|
|
locals
|
|
buf rb 4 ;ebp-4
|
|
endl
|
|
mov edi,[png_ptr]
|
|
cmp edi,0
|
|
je .end_f ;if (..==0) return
|
|
|
|
if PNG_IO_STATE_SUPPORTED eq 1
|
|
; Inform the I/O callback that the chunk CRC is being written.
|
|
; PNG_IO_CHUNK_CRC requires a single I/O function call.
|
|
|
|
mov dword[edi+png_struct.io_state], PNG_IO_WRITING or PNG_IO_CHUNK_CRC
|
|
end if
|
|
|
|
; Write the crc in a single operation
|
|
mov ebx,ebp
|
|
sub ebx,4
|
|
stdcall png_save_uint_32, ebx, [edi+png_struct.crc]
|
|
|
|
stdcall png_write_data, edi, ebx, 4
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; Write a PNG chunk all at once. The type is an array of ASCII characters
|
|
; representing the chunk name. The array must be at least 4 bytes in
|
|
; length, and does not need to be null terminated. To be safe, pass the
|
|
; pre-defined chunk names here, and if you need a new one, define it
|
|
; where the others are defined. The length is the length of the data.
|
|
; All the data must be present. If that is not possible, use the
|
|
; png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
|
|
; functions instead.
|
|
|
|
;void (png_structrp png_ptr, uint_32 chunk_name, bytep data, png_size_t length)
|
|
align 4
|
|
proc png_write_complete_chunk uses edi, png_ptr:dword, chunk_name:dword, p3data:dword, length:dword
|
|
mov edi,[png_ptr]
|
|
test edi,edi
|
|
jz .end_f ;if (..==0) return
|
|
|
|
; On 64-bit architectures 'length' may not fit in a uint_32.
|
|
cmp dword[length],PNG_UINT_31_MAX ;if(..>..)
|
|
jle @f
|
|
png_error edi, 'length exceeds PNG maximum'
|
|
@@:
|
|
stdcall png_write_chunk_header, edi, [chunk_name], [length]
|
|
stdcall png_write_chunk_data, edi, [p3data], [length]
|
|
stdcall png_write_chunk_end, edi
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; This is the API that calls the internal function above.
|
|
;void (png_structrp png_ptr, bytep chunk_string, bytep data, png_size_t length)
|
|
align 4
|
|
proc png_write_chunk, png_ptr:dword, chunk_string:dword, p3data:dword, length:dword
|
|
stdcall png_write_complete_chunk, [png_ptr], [chunk_string], [p3data], [length]
|
|
ret
|
|
endp
|
|
|
|
; This is used below to find the size of an image to pass to png_deflate_claim,
|
|
; so it only needs to be accurate if the size is less than 16384 bytes (the
|
|
; point at which a lower LZ window size can be used.)
|
|
|
|
;png_alloc_size_t (png_structrp png_ptr)
|
|
align 4
|
|
proc png_image_size uses ebx ecx edx edi esi, png_ptr:dword
|
|
; Only return sizes up to the maximum of a uint_32; do this by limiting
|
|
; the width and height used to 15 bits.
|
|
|
|
mov edi,[png_ptr]
|
|
mov ebx,[edi+png_struct.height]
|
|
|
|
cmp dword[edi+png_struct.rowbytes],32768
|
|
jge .end0
|
|
cmp ebx,32768
|
|
jge .end0 ;if (..<.. && ..<..)
|
|
cmp byte[edi+png_struct.interlaced],0
|
|
je .end1 ;if (..!=0)
|
|
; Interlacing makes the image larger because of the replication of
|
|
; both the filter byte and the padding to a byte boundary.
|
|
|
|
xor esi,esi
|
|
xor ecx,ecx
|
|
.cycle0:
|
|
PNG_PASS_COLS [edi+png_struct.width], ecx
|
|
;eax = pw
|
|
|
|
cmp eax,0
|
|
jle @f ;if (..>0)
|
|
mov edx,eax
|
|
movzx eax,byte[edi+png_struct.pixel_depth]
|
|
PNG_ROWBYTES eax, edx
|
|
inc eax
|
|
mov edx,eax
|
|
PNG_PASS_ROWS ebx, ecx
|
|
imul eax,edx
|
|
add esi,eax
|
|
@@:
|
|
inc ecx
|
|
cmp ecx,6
|
|
jle .cycle0
|
|
|
|
mov eax,esi
|
|
jmp .end_f
|
|
.end1: ;else
|
|
mov eax,[edi+png_struct.rowbytes]
|
|
inc eax
|
|
imul eax,ebx
|
|
jmp .end_f
|
|
.end0: ;else
|
|
mov eax,0xffffffff
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; This is the code to hack the first two bytes of the deflate stream (the
|
|
; deflate header) to correct the windowBits value to match the actual data
|
|
; size. Note that the second argument is the *uncompressed* size but the
|
|
; first argument is the *compressed* data (and it must be deflate
|
|
; compressed.)
|
|
|
|
;void (bytep data, png_alloc_size_t data_size)
|
|
align 4
|
|
proc optimize_cmf, p1data:dword, data_size:dword
|
|
; Optimize the CMF field in the zlib stream. The resultant zlib stream is
|
|
; still compliant to the stream specification.
|
|
png_debug 1, 'optimize_cmf'
|
|
pushad
|
|
cmp dword[data_size],16384
|
|
jg .end_f ;if (..<=..) ;else windowBits must be 15
|
|
mov esi,[p1data]
|
|
movzx ebx,byte[esi]
|
|
;ebx = z_cmf ;zlib compression method and flags
|
|
|
|
mov eax,ebx
|
|
and eax,0x0f
|
|
cmp eax,8
|
|
jne .end_f
|
|
mov eax,ebx
|
|
and eax,0xf0
|
|
cmp eax,0x70
|
|
jg .end_f ;if (..==.. && ..<=..)
|
|
;ecx = z_cinfo
|
|
;edi = half_z_window_size
|
|
|
|
mov ecx,ebx
|
|
shr ecx,4
|
|
xor edi,edi
|
|
inc edi
|
|
shl edi,7
|
|
shl edi,cl
|
|
|
|
cmp [data_size],edi
|
|
jg .end_f ;if (..<=..) ;else no change
|
|
.cycle0: ;do
|
|
shr edi,1
|
|
dec ecx
|
|
cmp ecx,0
|
|
jle @f
|
|
cmp [data_size],edi
|
|
jle .cycle0
|
|
@@: ;while (..>0 && ..<=..);
|
|
|
|
and ebx,0x0f
|
|
mov eax,ecx
|
|
shl eax,4
|
|
or ebx,eax
|
|
|
|
mov byte[esi],bl
|
|
movzx eax,byte[esi+1]
|
|
and eax,0xe0
|
|
shl ebx,8
|
|
add ebx,eax
|
|
add eax,0x1f
|
|
xchg eax,ebx
|
|
xor edx,edx
|
|
mov ecx,0x1f
|
|
div ecx
|
|
sub ebx,edx
|
|
mov byte[esi+1],bl
|
|
.end_f:
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
; Initialize the compressor for the appropriate type of compression.
|
|
;int (png_structrp png_ptr, uint_32 owner, png_alloc_size_t data_size)
|
|
;input:
|
|
; edi - png_ptr
|
|
align 4
|
|
proc png_deflate_claim uses ebx ecx, owner:dword, data_size:dword
|
|
locals
|
|
level dd ? ;int
|
|
method dd ? ;int
|
|
windowBits dd ? ;int
|
|
memLevel dd ? ;int
|
|
strategy dd ? ;int
|
|
msg rb 64 ;char[64]
|
|
endl
|
|
png_debug 1, 'in png_deflate_claim'
|
|
|
|
cmp dword[edi+png_struct.zowner],0
|
|
je .end0 ;if (..!=0)
|
|
mov ebx,ebp
|
|
sub ebx,64
|
|
if (PNG_WARNINGS_SUPPORTED eq 1) | (PNG_ERROR_TEXT_SUPPORTED eq 1)
|
|
mov eax,[owner]
|
|
mov [ebx],eax
|
|
mov word[ebx+4],': '
|
|
mov eax,[edi+png_struct.zowner]
|
|
mov [ebx+6],eax
|
|
; So the message that results is "<chunk> using zstream"; this is an
|
|
; internal error, but is very useful for debugging. i18n requirements
|
|
; are minimal.
|
|
|
|
cStr ,' using zstream'
|
|
stdcall png_safecat, ebx, 64, 10, eax
|
|
end if
|
|
if PNG_RELEASE_BUILD eq 1
|
|
png_warning edi, ebx
|
|
|
|
; Attempt sane error recovery
|
|
cmp dword[edi+png_struct.zowner],png_IDAT
|
|
jne @f ;if (..==.) ;don't steal from IDAT
|
|
cStr dword[edi+png_struct.zstream.msg],'in use by IDAT'
|
|
mov eax,Z_STREAM_ERROR
|
|
jmp .end_f
|
|
@@:
|
|
mov dword[edi+png_struct.zowner],0
|
|
else
|
|
png_error edi, ebx
|
|
end if
|
|
.end0:
|
|
|
|
mov eax,[edi+png_struct.zlib_level]
|
|
mov [level],eax
|
|
mov eax,[edi+png_struct.zlib_method]
|
|
mov [method],eax
|
|
mov eax,[edi+png_struct.zlib_window_bits]
|
|
mov [windowBits],eax
|
|
mov eax,[edi+png_struct.zlib_mem_level]
|
|
mov [memLevel],eax
|
|
|
|
cmp dword[owner],png_IDAT
|
|
jne .end1 ;if (..==..)
|
|
mov eax,[edi+png_struct.flags]
|
|
and eax,PNG_FLAG_ZLIB_CUSTOM_STRATEGY
|
|
jz @f ;if (..!=0)
|
|
mov eax,[edi+png_struct.zlib_strategy]
|
|
mov dword[strategy],eax
|
|
jmp .end2
|
|
@@:
|
|
cmp byte[edi+png_struct.do_filter],PNG_FILTER_NONE
|
|
je @f ;else if (..!=..)
|
|
mov dword[strategy],PNG_Z_DEFAULT_STRATEGY
|
|
jmp .end2
|
|
@@: ;else
|
|
mov dword[strategy],PNG_Z_DEFAULT_NOFILTER_STRATEGY
|
|
jmp .end2
|
|
.end1: ;else
|
|
if PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED eq 1
|
|
mov eax,[edi+png_struct.zlib_text_level]
|
|
mov [level],eax
|
|
mov eax,[edi+png_struct.zlib_text_method]
|
|
mov [method],eax
|
|
mov eax,[edi+png_struct.zlib_text_window_bits]
|
|
mov [windowBits],eax
|
|
mov eax,[edi+png_struct.zlib_text_mem_level]
|
|
mov [memLevel],eax
|
|
mov eax,[edi+png_struct.zlib_text_strategy]
|
|
mov [strategy],eax
|
|
else
|
|
; If customization is not supported the values all come from the
|
|
; IDAT values except for the strategy, which is fixed to the
|
|
; default. (This is the pre-1.6.0 behavior too, although it was
|
|
; implemented in a very different way.)
|
|
|
|
mov dword[strategy],Z_DEFAULT_STRATEGY
|
|
end if
|
|
.end2:
|
|
|
|
; Adjust 'windowBits' down if larger than 'data_size'; to stop this
|
|
; happening just pass 32768 as the data_size parameter. Notice that zlib
|
|
; requires an extra 262 bytes in the window in addition to the data to be
|
|
; able to see the whole of the data, so if data_size+262 takes us to the
|
|
; next windowBits size we need to fix up the value later. (Because even
|
|
; though deflate needs the extra window, inflate does not!)
|
|
|
|
cmp dword[data_size],16384
|
|
jg .end3 ;if (..<=..)
|
|
; IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to
|
|
; work round a Microsoft Visual C misbehavior which, contrary to C-90,
|
|
; widens the result of the following shift to 64-bits if (and,
|
|
; apparently, only if) it is used in a test.
|
|
|
|
mov ecx,[windowBits]
|
|
dec ecx
|
|
xor eax,eax
|
|
inc eax
|
|
shl eax,cl ;eax = half_window_size
|
|
mov ecx,[data_size]
|
|
add ecx,262
|
|
@@: ;while (..<=..)
|
|
cmp ecx,eax
|
|
jg .end3
|
|
shr eax,1
|
|
dec dword[windowBits]
|
|
jmp @b
|
|
.end3:
|
|
|
|
; Check against the previous initialized values, if any.
|
|
mov eax,[edi+png_struct.flags]
|
|
and eax,PNG_FLAG_ZSTREAM_INITIALIZED
|
|
jz .end4
|
|
mov eax,[level]
|
|
cmp [edi+png_struct.zlib_set_level],eax
|
|
jne @f
|
|
mov eax,[method]
|
|
cmp [edi+png_struct.zlib_set_method],eax
|
|
jne @f
|
|
mov eax,[windowBits]
|
|
cmp [edi+png_struct.zlib_set_window_bits],eax
|
|
jne @f
|
|
mov eax,[memLevel]
|
|
cmp [edi+png_struct.zlib_set_mem_level],eax
|
|
jne @f
|
|
mov eax,[strategy]
|
|
cmp [edi+png_struct.zlib_set_strategy],eax
|
|
je .end4
|
|
@@: ;if (..!=0 && (..!=.. || ..!=.. || ..!=.. || ..!=.. || ..!=..))
|
|
mov eax,edi
|
|
add eax,png_struct.zstream
|
|
stdcall [deflateEnd], eax
|
|
cmp eax,Z_OK
|
|
je @f ;if (..!=..)
|
|
png_warning edi, 'deflateEnd failed (ignored)'
|
|
@@:
|
|
and dword[edi+png_struct.flags], not PNG_FLAG_ZSTREAM_INITIALIZED
|
|
.end4:
|
|
|
|
; For safety clear out the input and output pointers (currently zlib
|
|
; doesn't use them on Init, but it might in the future).
|
|
|
|
mov dword[edi+png_struct.zstream.next_in],0
|
|
mov dword[edi+png_struct.zstream.avail_in],0
|
|
mov dword[edi+png_struct.zstream.next_out],0
|
|
mov dword[edi+png_struct.zstream.avail_out],0
|
|
|
|
; Now initialize if required, setting the new parameters, otherwise just
|
|
; to a simple reset to the previous parameters.
|
|
|
|
mov ecx,edi
|
|
add ecx,png_struct.zstream
|
|
mov eax,[edi+png_struct.flags]
|
|
and eax,PNG_FLAG_ZSTREAM_INITIALIZED
|
|
jz @f ;if (..!=0)
|
|
stdcall [deflateReset], ecx
|
|
jmp .end5
|
|
@@: ;else
|
|
stdcall [deflateInit2], ecx, [level], [method], [windowBits],\
|
|
[memLevel], [strategy]
|
|
|
|
cmp eax,Z_OK
|
|
je .end5 ;if (..==..)
|
|
or dword[edi+png_struct.flags],PNG_FLAG_ZSTREAM_INITIALIZED
|
|
.end5:
|
|
|
|
; The return code is from either deflateReset or deflateInit2; they have
|
|
; pretty much the same set of error codes.
|
|
|
|
cmp eax,Z_OK
|
|
jne @f ;if (..==..)
|
|
mov ecx,[owner]
|
|
mov [edi+png_struct.zowner],ecx
|
|
jmp .end_f
|
|
@@: ;else
|
|
stdcall png_zstream_error, edi, eax
|
|
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; Clean up (or trim) a linked list of compression buffers.
|
|
;void (png_structrp png_ptr, png_compression_bufferp *listp)
|
|
align 4
|
|
proc png_free_buffer_list uses eax ebx ecx edi, png_ptr:dword, listp:dword
|
|
mov eax,[listp]
|
|
mov ebx,[eax]
|
|
;eax = png_compression_bufferp list
|
|
|
|
cmp ebx,0
|
|
je @f ;if (..!=0)
|
|
mov dword[eax],0
|
|
.cycle0: ;do
|
|
mov ecx,[ebx+png_compression_buffer.next]
|
|
stdcall png_free, edi, ebx
|
|
mov ebx,ecx
|
|
cmp ebx,0
|
|
jne .cycle0 ;while (..!=0)
|
|
@@:
|
|
ret
|
|
endp
|
|
|
|
if PNG_WRITE_COMPRESSED_TEXT_SUPPORTED eq 1
|
|
; This pair of functions encapsulates the operation of (a) compressing a
|
|
; text string, and (b) issuing it later as a series of chunk data writes.
|
|
; The compression_state structure is shared context for these functions
|
|
; set up by the caller to allow access to the relevant local variables.
|
|
|
|
; compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size
|
|
; temporary buffers. From 1.6.0 it is retained in png_struct so that it will
|
|
; be correctly freed in the event of a write error (previous implementations
|
|
; just leaked memory.)
|
|
|
|
struct compression_state
|
|
input dd ? ;bytep ;The uncompressed input data
|
|
input_len dd ? ;png_alloc_size_t ;Its length
|
|
output_len dd ? ;uint_32 ;Final compressed length
|
|
output rb 1024 ;byte[1024] ;First block of output
|
|
ends
|
|
|
|
;void (compression_state *comp, bytep input, png_alloc_size_t input_len)
|
|
align 4
|
|
proc png_text_compress_init uses eax ebx, comp:dword, input:dword, input_len:dword
|
|
mov ebx,[comp]
|
|
mov eax,[input]
|
|
mov [ebx+compression_state.input],eax
|
|
mov eax,[input_len]
|
|
mov [ebx+compression_state.input_len],eax
|
|
mov dword[ebx+compression_state.output_len],0
|
|
ret
|
|
endp
|
|
|
|
; Compress the data in the compression state input
|
|
;int (png_structrp png_ptr, uint_32 chunk_name, compression_state *comp, uint_32 prefix_len)
|
|
align 4
|
|
proc png_text_compress uses ebx ecx edx edi esi, png_ptr:dword, chunk_name:dword, comp:dword, prefix_len:dword
|
|
locals
|
|
output_len dd ? ;uint_32
|
|
avail_in dd ? ;uInt
|
|
next dd ? ;png_compression_buffer*
|
|
endl
|
|
; To find the length of the output it is necessary to first compress the
|
|
; input. The result is buffered rather than using the two-pass algorithm
|
|
; that is used on the inflate side; deflate is assumed to be slower and a
|
|
; PNG writer is assumed to have more memory available than a PNG reader.
|
|
|
|
; IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an
|
|
; upper limit on the output size, but it is always bigger than the input
|
|
; size so it is likely to be more efficient to use this linked-list
|
|
; approach.
|
|
|
|
mov ebx,[comp]
|
|
mov edi,[png_ptr]
|
|
stdcall png_deflate_claim, [chunk_name], [ebx+compression_state.input_len]
|
|
|
|
cmp eax,Z_OK
|
|
jne .end_f ;if (..!=Z_OK) return ..
|
|
|
|
; Set up the compression buffers, we need a loop here to avoid overflowing a
|
|
; uInt. Use ZLIB_IO_MAX to limit the input. The output is always limited
|
|
; by the output buffer size, so there is no need to check that. Since this
|
|
; is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits
|
|
; in size.
|
|
|
|
mov edx,edi
|
|
add edx,png_struct.zbuffer_list
|
|
mov ecx,[ebx+compression_state.input_len] ;may be zero!
|
|
;ecx = input_len
|
|
;edx = end
|
|
;esi = ret
|
|
|
|
; zlib updates these for us:
|
|
mov eax,[ebx+compression_state.input]
|
|
mov [edi+png_struct.zstream.next_in],eax
|
|
mov dword[edi+png_struct.zstream.avail_in],0 ;Set below
|
|
mov eax,ebx
|
|
add eax,compression_state.output
|
|
mov [edi+png_struct.zstream.next_out],eax
|
|
mov eax,sizeof.compression_state.output ;1024
|
|
mov [edi+png_struct.zstream.avail_out],eax
|
|
|
|
mov [output_len],eax
|
|
|
|
.cycle0: ;do
|
|
mov dword[avail_in],ZLIB_IO_MAX
|
|
|
|
cmp [avail_in],ecx
|
|
jle @f ;if (..>..)
|
|
mov [avail_in],ecx
|
|
@@:
|
|
sub ecx,[avail_in]
|
|
|
|
mov eax,[avail_in]
|
|
mov [edi+png_struct.zstream.avail_in],eax
|
|
|
|
cmp dword[edi+png_struct.zstream.avail_out],0
|
|
jne .end0 ;if (..==0)
|
|
; Chunk data is limited to 2^31 bytes in length, so the prefix
|
|
; length must be counted here.
|
|
|
|
mov eax,[output_len]
|
|
add eax,[prefix_len]
|
|
cmp eax,PNG_UINT_31_MAX
|
|
jle @f ;if (..>..)
|
|
mov esi,Z_MEM_ERROR
|
|
jmp .cycle0end
|
|
@@:
|
|
|
|
; Need a new (malloc'ed) buffer, but there may be one present
|
|
; already.
|
|
|
|
mov eax,[edx]
|
|
mov [next],eax
|
|
|
|
test eax,eax
|
|
jnz .end1 ;if (..==0)
|
|
PNG_COMPRESSION_BUFFER_SIZE edi
|
|
stdcall png_malloc, edi, eax
|
|
mov [next],eax
|
|
|
|
test eax,eax
|
|
jnz @f ;if (..==0)
|
|
mov esi,Z_MEM_ERROR
|
|
jmp .cycle0end
|
|
@@:
|
|
|
|
; Link in this buffer (so that it will be freed later)
|
|
mov dword[eax+png_compression_buffer.next],0
|
|
mov [edx],eax
|
|
.end1:
|
|
|
|
mov eax,[next]
|
|
mov eax,[eax+png_compression_buffer.output]
|
|
mov [edi+png_struct.zstream.next_out],eax
|
|
mov eax,[edi+png_struct.zbuffer_size]
|
|
mov [edi+png_struct.zstream.avail_out],eax
|
|
add [output_len],eax
|
|
|
|
; Move 'end' to the next buffer pointer.
|
|
mov eax,[next]
|
|
add eax,png_compression_buffer.next
|
|
mov edx,eax
|
|
.end0:
|
|
|
|
; Compress the data
|
|
mov eax,Z_FINISH
|
|
cmp dword[input_len],0
|
|
jle @f
|
|
mov eax,Z_NO_FLUSH
|
|
@@:
|
|
push eax
|
|
mov eax,edi
|
|
add eax,png_struct.zstream
|
|
stdcall [deflate], eax ;, ...
|
|
mov esi,eax
|
|
|
|
; Claw back input data that was not consumed (because avail_in is
|
|
; reset above every time round the loop).
|
|
|
|
mov eax,[edi+png_struct.zstream.avail_in]
|
|
add [input_len],eax
|
|
mov dword[edi+png_struct.zstream.avail_in],0 ;safety
|
|
cmp esi,Z_OK
|
|
je .cycle0 ;while (..==..)
|
|
.cycle0end:
|
|
|
|
; There may be some space left in the last output buffer. This needs to
|
|
; be subtracted from output_len.
|
|
|
|
mov eax,[edi+png_struct.zstream.avail_out]
|
|
sub [output_len],eax
|
|
mov dword[edi+png_struct.zstream.avail_out],0 ;safety
|
|
mov eax,[output_len]
|
|
mov [ebx+compression_state.output_len],eax
|
|
|
|
; Now double check the output length, put in a custom message if it is
|
|
; too long. Otherwise ensure the z_stream::msg pointer is set to
|
|
; something.
|
|
|
|
mov eax,[output_len]
|
|
add eax,[prefix_len]
|
|
cmp eax,PNG_UINT_31_MAX
|
|
jl @f ;if (..>=..)
|
|
cStr dword[edi+png_struct.zstream.msg],'compressed data too long'
|
|
mov esi,Z_MEM_ERROR
|
|
jmp .end2
|
|
@@: ;else
|
|
stdcall png_zstream_error, edi, esi
|
|
.end2:
|
|
|
|
; Reset zlib for another zTXt/iTXt or image data
|
|
mov dword[edi+png_struct.zowner],0
|
|
|
|
; The only success case is Z_STREAM_END, input_len must be 0; if not this
|
|
; is an internal error.
|
|
|
|
cmp esi,Z_STREAM_END
|
|
jne @f
|
|
cmp dword[input_len],0
|
|
jne @f ;if (..==.. && ..==0)
|
|
if PNG_WRITE_OPTIMIZE_CMF_SUPPORTED eq 1
|
|
; Fix up the deflate header, if required
|
|
mov eax,ebx
|
|
add eax,compression_state.output
|
|
stdcall optimize_cmf, eax, [ebx+compression_state.input_len]
|
|
end if
|
|
; But Z_OK is returned, not Z_STREAM_END; this allows the claim
|
|
; function above to return Z_STREAM_END on an error (though it never
|
|
; does in the current versions of zlib.)
|
|
|
|
mov eax,Z_OK
|
|
jmp .end_f
|
|
@@: ;else
|
|
mov eax,esi
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; Ship the compressed text out via chunk writes
|
|
;void (png_structrp png_ptr, compression_state *comp)
|
|
align 4
|
|
proc png_write_compressed_data_out uses ebx edi, png_ptr:dword, comp:dword
|
|
locals
|
|
output_len dd ? ;uint_32 ;= comp.output_len
|
|
output dd ? ;bytep ;= comp.output
|
|
avail dd ? ;uint_32 ;= sizeof.comp.output
|
|
next dd ? ;png_compression_buffer* ;= png_ptr.zbuffer_list
|
|
endl
|
|
mov ebx,[comp]
|
|
mov eax,[ebx+compression_state.output_len]
|
|
mov [output_len],eax
|
|
mov eax,ebx
|
|
add eax,compression_state.output
|
|
mov [output],eax
|
|
mov [avail],sizeof.compression_state.output ;1024
|
|
mov edi,[png_ptr]
|
|
mov eax,[edi+png_struct.zbuffer_list]
|
|
mov [next],eax
|
|
|
|
.cycle0: ;for (;;)
|
|
mov eax,[output_len]
|
|
cmp [avail],eax
|
|
jle @f ;if (..>..)
|
|
mov [avail],eax
|
|
@@:
|
|
|
|
stdcall png_write_chunk_data, edi, [output], [avail]
|
|
|
|
mov [avail],eax
|
|
sub [output_len],eax
|
|
|
|
cmp dword[output_len],0
|
|
je .cycle0end
|
|
cmp dword[next],0
|
|
je .cycle0end ;if (..==0 || ..==0) break
|
|
|
|
mov eax,[edi+png_struct.zbuffer_size]
|
|
mov [avail],eax
|
|
mov eax,[next]
|
|
add eax,png_compression_buffer.output
|
|
mov [output],eax
|
|
mov eax,[next]
|
|
mov eax,[eax+png_compression_buffer.next]
|
|
mov [next],eax
|
|
jmp .cycle0
|
|
.cycle0end:
|
|
|
|
; This is an internal error; 'next' must have been NULL!
|
|
cmp dword[output_len],0
|
|
jle @f ;if (..>0)
|
|
png_error edi, 'error writing ancillary chunked compressed data'
|
|
@@:
|
|
ret
|
|
endp
|
|
end if ;WRITE_COMPRESSED_TEXT
|
|
|
|
; Write the IHDR chunk, and update the png_struct with the necessary
|
|
; information. Note that the rest of this code depends upon this
|
|
; information being correct.
|
|
|
|
;void (png_structrp png_ptr, uint_32 width, uint_32 height,
|
|
; int bit_depth, int color_type, int compression_type, int filter_type, int interlace_type)
|
|
align 4
|
|
proc png_write_IHDR, png_ptr:dword, width:dword, height:dword, bit_depth:dword,\
|
|
color_type:dword, compression_type:dword, filter_type:dword, interlace_type:dword
|
|
locals
|
|
buf rb 13 ;byte[13] ;Buffer to store the IHDR info
|
|
endl
|
|
png_debug 1, 'in png_write_IHDR'
|
|
pushad
|
|
; Check that we have valid input data from the application info
|
|
mov edi,[png_ptr]
|
|
movzx ebx,byte[color_type]
|
|
cmp ebx,PNG_COLOR_TYPE_GRAY
|
|
jne .end_0
|
|
cmp byte[bit_depth],1
|
|
je @f
|
|
cmp byte[bit_depth],2
|
|
je @f
|
|
cmp byte[bit_depth],4
|
|
je @f
|
|
cmp byte[bit_depth],8
|
|
je @f
|
|
if PNG_WRITE_16BIT_SUPPORTED eq 1
|
|
cmp byte[bit_depth],16
|
|
je @f
|
|
end if
|
|
jmp .def_0
|
|
@@:
|
|
mov byte[edi+png_struct.channels], 1
|
|
jmp .end_s0
|
|
.def_0: ;default
|
|
png_error edi, 'Invalid bit depth for grayscale image'
|
|
jmp .end_s0
|
|
.end_0:
|
|
|
|
cmp ebx,PNG_COLOR_TYPE_RGB
|
|
jne .end_1
|
|
cmp byte[bit_depth],8
|
|
je @f ;if (..!=8)
|
|
if PNG_WRITE_16BIT_SUPPORTED eq 1
|
|
cmp byte[bit_depth],16
|
|
je @f ;if (..!=16)
|
|
end if
|
|
png_error edi, 'Invalid bit depth for RGB image'
|
|
@@:
|
|
mov byte[edi+png_struct.channels], 3
|
|
jmp .end_s0
|
|
.end_1:
|
|
|
|
cmp ebx,PNG_COLOR_TYPE_PALETTE
|
|
jne .end_2
|
|
cmp byte[bit_depth],1
|
|
je @f
|
|
cmp byte[bit_depth],2
|
|
je @f
|
|
cmp byte[bit_depth],4
|
|
je @f
|
|
cmp byte[bit_depth],8
|
|
je @f
|
|
jmp .def_1
|
|
@@:
|
|
mov byte[edi+png_struct.channels], 1
|
|
jmp .end_s0
|
|
.def_1: ;default
|
|
png_error edi, 'Invalid bit depth for paletted image'
|
|
jmp .end_s0
|
|
.end_2:
|
|
|
|
cmp ebx,PNG_COLOR_TYPE_GRAY_ALPHA
|
|
jne .end_3
|
|
cmp byte[bit_depth],8
|
|
je @f ;if (..!=8)
|
|
cmp byte[bit_depth],16
|
|
je @f ;if (..!=16)
|
|
png_error edi, 'Invalid bit depth for grayscale+alpha image'
|
|
@@:
|
|
mov byte[edi+png_struct.channels], 2
|
|
jmp .end_s0
|
|
.end_3:
|
|
|
|
cmp ebx,PNG_COLOR_TYPE_RGB_ALPHA
|
|
jne .end_4
|
|
cmp byte[bit_depth],8
|
|
je @f ;if (..!=8)
|
|
if PNG_WRITE_16BIT_SUPPORTED eq 1
|
|
cmp byte[bit_depth],16
|
|
je @f ;if (..!=16)
|
|
end if
|
|
png_error edi, 'Invalid bit depth for RGBA image'
|
|
@@:
|
|
mov byte[edi+png_struct.channels], 4
|
|
jmp .end_s0
|
|
.end_4:
|
|
|
|
;default:
|
|
png_error edi, 'Invalid image color type specified'
|
|
.end_s0:
|
|
|
|
cmp byte[compression_type], PNG_COMPRESSION_TYPE_BASE
|
|
je @f ;if (..!=..)
|
|
png_warning edi, 'Invalid compression type specified'
|
|
mov dword[compression_type], PNG_COMPRESSION_TYPE_BASE
|
|
@@:
|
|
|
|
; Write filter_method 64 (intrapixel differencing) only if
|
|
; 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
|
|
; 2. Libpng did not write a PNG signature (this filter_method is only
|
|
; used in PNG datastreams that are embedded in MNG datastreams) and
|
|
; 3. The application called png_permit_mng_features with a mask that
|
|
; included PNG_FLAG_MNG_FILTER_64 and
|
|
; 4. The filter_method is 64 and
|
|
; 5. The color_type is RGB or RGBA
|
|
|
|
; if (
|
|
if PNG_MNG_FEATURES_SUPPORTED eq 1
|
|
; !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) != 0 &&
|
|
; ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) &&
|
|
; (color_type == PNG_COLOR_TYPE_RGB ||
|
|
; color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
|
|
; (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
|
|
end if
|
|
cmp dword[filter_type],PNG_FILTER_TYPE_BASE
|
|
je @f ;if (..!=..)
|
|
png_warning edi, 'Invalid filter type specified'
|
|
mov dword[filter_type], PNG_FILTER_TYPE_BASE
|
|
@@:
|
|
|
|
if PNG_WRITE_INTERLACING_SUPPORTED eq 1
|
|
cmp dword[interlace_type],PNG_INTERLACE_NONE
|
|
je @f ;if (..!=..)
|
|
cmp dword[interlace_type],PNG_INTERLACE_ADAM7
|
|
je @f ;if (..!=..)
|
|
png_warning edi, 'Invalid interlace type specified'
|
|
mov dword[interlace_type], PNG_INTERLACE_ADAM7
|
|
@@:
|
|
else
|
|
mov dword[interlace_type], PNG_INTERLACE_NONE
|
|
end if
|
|
|
|
; Save the relevant information
|
|
mov al,byte[bit_depth]
|
|
mov byte[edi+png_struct.bit_depth],al
|
|
mov al,byte[color_type]
|
|
mov byte[edi+png_struct.color_type],al
|
|
mov al,byte[interlace_type]
|
|
mov byte[edi+png_struct.interlaced],al
|
|
if PNG_MNG_FEATURES_SUPPORTED eq 1
|
|
mov al,byte[filter_type]
|
|
mov byte[edi+png_struct.filter_type],al
|
|
end if
|
|
mov al,byte[compression_type]
|
|
mov byte[edi+png_struct.compression_type],al
|
|
mov eax,[width]
|
|
mov [edi+png_struct.width],eax
|
|
mov eax,[height]
|
|
mov [edi+png_struct.height],eax
|
|
|
|
movzx eax,byte[edi+png_struct.channels]
|
|
imul ax,word[bit_depth]
|
|
mov byte[edi+png_struct.pixel_depth],al
|
|
PNG_ROWBYTES eax, [width]
|
|
mov [edi+png_struct.rowbytes],eax
|
|
; Set the usr info, so any transformations can modify it
|
|
mov eax,[edi+png_struct.width]
|
|
mov [edi+png_struct.usr_width],eax
|
|
mov al,[edi+png_struct.bit_depth]
|
|
mov [edi+png_struct.usr_bit_depth],al
|
|
mov al,[edi+png_struct.channels]
|
|
mov [edi+png_struct.usr_channels],al
|
|
|
|
; Pack the header information into the buffer
|
|
mov ebx,ebp
|
|
sub ebx,13
|
|
stdcall png_save_uint_32, ebx, [width]
|
|
add ebx,4
|
|
stdcall png_save_uint_32, ebx, [height]
|
|
add ebx,4
|
|
mov al,byte[bit_depth]
|
|
mov byte[ebx],al ;buf[8] = (byte)bit_depth
|
|
inc ebx
|
|
mov al,byte[color_type]
|
|
mov byte[ebx],al ;buf[9] = (byte)color_type
|
|
inc ebx
|
|
mov al,byte[compression_type]
|
|
mov byte[ebx],al ;buf[10] = (byte)compression_type
|
|
inc ebx
|
|
mov al,byte[filter_type]
|
|
mov byte[ebx],al ;buf[11] = (byte)filter_type
|
|
inc ebx
|
|
mov al,byte[interlace_type]
|
|
mov byte[ebx],al ;buf[12] = (byte)interlace_type
|
|
sub ebx,12
|
|
|
|
; Write the chunk
|
|
stdcall png_write_complete_chunk, edi, png_IHDR, ebx, dword 13
|
|
|
|
cmp byte[edi+png_struct.do_filter],PNG_NO_FILTERS
|
|
jne .end_5 ;if (..==..)
|
|
|
|
cmp byte[edi+png_struct.color_type],PNG_COLOR_TYPE_PALETTE
|
|
je @f
|
|
cmp byte[edi+png_struct.bit_depth],8
|
|
jl @f ;if ((..==..)||(..<..))
|
|
jmp .els_5
|
|
@@:
|
|
mov byte[edi+png_struct.do_filter], PNG_FILTER_NONE
|
|
jmp .end_5
|
|
.els_5: ;else
|
|
mov byte[edi+png_struct.do_filter], PNG_ALL_FILTERS
|
|
.end_5:
|
|
|
|
mov dword[edi+png_struct.mode], PNG_HAVE_IHDR ;not READY_FOR_ZTXT
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
; Write the palette. We are careful not to trust png_color to be in the
|
|
; correct order for PNG, so people can redefine it to any convenient
|
|
; structure.
|
|
|
|
;void (png_structrp png_ptr, png_const_colorp palette, uint_32 num_pal)
|
|
align 4
|
|
proc png_write_PLTE, png_ptr:dword, palette:dword, num_pal:dword
|
|
locals
|
|
;max_palette_length dd ? ;uint_32
|
|
i dd ?
|
|
pal_ptr dd ? ;png_const_colorp
|
|
buf rb 3 ;byte[3]
|
|
endl
|
|
png_debug 1, 'in png_write_PLTE'
|
|
|
|
pushad
|
|
mov edi,[png_ptr]
|
|
movzx eax,byte[edi+png_struct.color_type]
|
|
cmp eax,PNG_COLOR_TYPE_PALETTE
|
|
je @f ;if (..==..)
|
|
;mov dword[max_palette_length],PNG_MAX_PALETTE_LENGTH
|
|
mov eax,PNG_MAX_PALETTE_LENGTH
|
|
jmp .end0
|
|
@@:
|
|
mov cl,byte[edi+png_struct.bit_depth]
|
|
xor eax,eax
|
|
inc eax
|
|
shl eax,cl
|
|
;mov [max_palette_length],eax
|
|
.end0:
|
|
|
|
if PNG_MNG_FEATURES_SUPPORTED eq 1
|
|
cmp [num_pal],eax
|
|
jg @f
|
|
mov eax,[edi+png_struct.mng_features_permitted]
|
|
and eax,PNG_FLAG_MNG_EMPTY_PLTE
|
|
jnz .end1
|
|
cmp [num_pal],0
|
|
jne .end1
|
|
@@:
|
|
end if
|
|
|
|
cmp byte[edi+png_struct.color_type],PNG_COLOR_TYPE_PALETTE ;if (..==..)
|
|
jne @f
|
|
png_error edi, 'Invalid number of colors in palette'
|
|
jmp .end1
|
|
@@: ;else
|
|
png_warning edi, 'Invalid number of colors in palette'
|
|
jmp .end_f
|
|
.end1:
|
|
|
|
movzx eax,byte[edi+png_struct.color_type]
|
|
and eax,PNG_COLOR_MASK_COLOR
|
|
jnz @f ;if (..==0)
|
|
png_warning edi, 'Ignoring request to write a PLTE chunk in grayscale PNG'
|
|
jmp .end_f
|
|
@@:
|
|
|
|
movzx eax,word[num_pal]
|
|
mov word[edi+png_struct.num_palette],ax
|
|
png_debug1 3, 'num_palette = %d', eax
|
|
|
|
imul eax,3
|
|
stdcall png_write_chunk_header, edi, png_PLTE, eax
|
|
if PNG_POINTER_INDEXING_SUPPORTED eq 1
|
|
|
|
; for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
|
|
; {
|
|
; buf[0] = pal_ptr->red;
|
|
; buf[1] = pal_ptr->green;
|
|
; buf[2] = pal_ptr->blue;
|
|
; png_write_chunk_data(png_ptr, buf, (png_size_t)3);
|
|
; }
|
|
|
|
else
|
|
; This is a little slower but some buggy compilers need to do this
|
|
; instead
|
|
|
|
; pal_ptr=palette;
|
|
|
|
; for (i = 0; i < num_pal; i++)
|
|
; {
|
|
; buf[0] = pal_ptr[i].red;
|
|
; buf[1] = pal_ptr[i].green;
|
|
; buf[2] = pal_ptr[i].blue;
|
|
; png_write_chunk_data(png_ptr, buf, (png_size_t)3);
|
|
; }
|
|
|
|
end if
|
|
stdcall png_write_chunk_end, edi
|
|
or dword[edi+png_struct.mode], PNG_HAVE_PLTE
|
|
.end_f:
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
; This is similar to png_text_compress, above, except that it does not require
|
|
; all of the data at once and, instead of buffering the compressed result,
|
|
; writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out
|
|
; because it calls the write interface. As a result it does its own error
|
|
; reporting and does not return an error code. In the event of error it will
|
|
; just call png_error. The input data length may exceed 32-bits. The 'flush'
|
|
; parameter is exactly the same as that to deflate, with the following
|
|
; meanings:
|
|
|
|
; Z_NO_FLUSH: normal incremental output of compressed data
|
|
; Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush
|
|
; Z_FINISH: this is the end of the input, do a Z_FINISH and clean up
|
|
|
|
; The routine manages the acquire and release of the png_ptr->zstream by
|
|
; checking and (at the end) clearing png_ptr->zowner; it does some sanity
|
|
; checks on the 'mode' flags while doing this.
|
|
|
|
;void (png_structrp png_ptr, bytep input, png_alloc_size_t input_len, int flush)
|
|
;input:
|
|
; edi - png_ptr
|
|
align 4
|
|
proc png_compress_IDAT uses eax ebx ecx edx, input:dword, input_len:dword, flush:dword
|
|
png_debug 1, 'in png_compress_IDAT'
|
|
|
|
cmp dword[edi+png_struct.zowner],png_IDAT
|
|
je .end0 ;if (..!=..)
|
|
; First time. Ensure we have a temporary buffer for compression and
|
|
; trim the buffer list if it has more than one entry to free memory.
|
|
; If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been
|
|
; created at this point, but the check here is quick and safe.
|
|
|
|
cmp dword[edi+png_struct.zbuffer_list],0
|
|
jne @f ;if (..==0)
|
|
PNG_COMPRESSION_BUFFER_SIZE edi
|
|
stdcall png_malloc, edi, eax
|
|
mov [edi+png_struct.zbuffer_list],eax
|
|
mov dword[eax+png_compression_buffer.next],0
|
|
jmp .end1
|
|
@@: ;else
|
|
mov eax,[edi+png_struct.zbuffer_list]
|
|
add eax,png_compression_buffer.next
|
|
;eax = &...next
|
|
stdcall png_free_buffer_list, edi, eax
|
|
.end1:
|
|
|
|
;It is a terminal error if we can't claim the zstream.
|
|
stdcall png_image_size, edi
|
|
stdcall png_deflate_claim, png_IDAT, eax
|
|
cmp eax,Z_OK
|
|
je @f ;if (..!=..)
|
|
png_error edi, [edi+png_struct.zstream.msg]
|
|
@@:
|
|
|
|
; The output state is maintained in png_ptr->zstream, so it must be
|
|
; initialized here after the claim.
|
|
|
|
mov eax,[edi+png_struct.zbuffer_list]
|
|
add eax,png_compression_buffer.output
|
|
mov [edi+png_struct.zstream.next_out],eax
|
|
mov eax,[edi+png_struct.zbuffer_size]
|
|
mov [edi+png_struct.zstream.avail_out],eax
|
|
.end0:
|
|
|
|
; Now loop reading and writing until all the input is consumed or an error
|
|
; terminates the operation. The _out values are maintained across calls to
|
|
; this function, but the input must be reset each time.
|
|
|
|
mov eax,[input]
|
|
mov [edi+png_struct.zstream.next_in],eax
|
|
mov dword[edi+png_struct.zstream.avail_in],0 ;set below
|
|
align 4
|
|
.cycle0:
|
|
;INPUT: from the row data
|
|
mov eax,ZLIB_IO_MAX
|
|
|
|
cmp eax,[input_len]
|
|
jbe @f ;if (..>..)
|
|
mov eax,[input_len] ;safe because of the check
|
|
@@:
|
|
|
|
mov [edi+png_struct.zstream.avail_in],eax
|
|
sub [input_len],eax
|
|
|
|
mov eax,[flush]
|
|
cmp dword[input_len],0
|
|
jle @f
|
|
mov eax,Z_NO_FLUSH
|
|
@@:
|
|
mov ecx,edi
|
|
add ecx,png_struct.zstream
|
|
stdcall [deflate], ecx, eax
|
|
mov ebx,eax
|
|
|
|
;Include as-yet unconsumed input
|
|
mov eax,[edi+png_struct.zstream.avail_in]
|
|
add [input_len],eax
|
|
mov dword[edi+png_struct.zstream.avail_in],0
|
|
|
|
; OUTPUT: write complete IDAT chunks when avail_out drops to zero. Note
|
|
; that these two zstream fields are preserved across the calls, therefore
|
|
; there is no need to set these up on entry to the loop.
|
|
|
|
cmp dword[edi+png_struct.zstream.avail_out],0
|
|
jne .end2 ;if (..==0)
|
|
mov edx,[edi+png_struct.zbuffer_list]
|
|
add edx,png_compression_buffer.output
|
|
mov ecx,[edi+png_struct.zbuffer_size]
|
|
;edx = data
|
|
;ecx = size
|
|
; Write an IDAT containing the data then reset the buffer. The
|
|
; first IDAT may need deflate header optimization.
|
|
|
|
if PNG_WRITE_OPTIMIZE_CMF_SUPPORTED eq 1
|
|
mov eax,[edi+png_struct.mode]
|
|
and eax,PNG_HAVE_IDAT
|
|
jnz @f
|
|
cmp byte[edi+png_struct.compression_type],PNG_COMPRESSION_TYPE_BASE
|
|
jne @f ;if (..==0 && ..==..)
|
|
stdcall png_image_size, edi
|
|
stdcall optimize_cmf, edx, eax
|
|
@@:
|
|
end if
|
|
|
|
stdcall png_write_complete_chunk, edi, png_IDAT, edx, ecx
|
|
or dword[edi+png_struct.mode],PNG_HAVE_IDAT
|
|
|
|
mov [edi+png_struct.zstream.next_out],edx
|
|
mov [edi+png_struct.zstream.avail_out],ecx
|
|
|
|
; For SYNC_FLUSH or FINISH it is essential to keep calling zlib with
|
|
; the same flush parameter until it has finished output, for NO_FLUSH
|
|
; it doesn't matter.
|
|
|
|
cmp ebx,Z_OK
|
|
jne .end2
|
|
cmp dword[flush],Z_NO_FLUSH
|
|
jne .cycle0 ;if (..==.. && ..!=..) continue
|
|
.end2:
|
|
|
|
; The order of these checks doesn't matter much; it just affects which
|
|
; possible error might be detected if multiple things go wrong at once.
|
|
|
|
cmp ebx,Z_OK
|
|
jne .end3 ;if (..==..) ;most likely return code!
|
|
; If all the input has been consumed then just return. If Z_FINISH
|
|
; was used as the flush parameter something has gone wrong if we get
|
|
; here.
|
|
|
|
cmp dword[input_len],0
|
|
jne .cycle0 ;if (..==0)
|
|
cmp dword[flush],Z_FINISH
|
|
jne .cycle0end ;if (..==..)
|
|
png_error edi, 'Z_OK on Z_FINISH with output space'
|
|
jmp .cycle0end
|
|
.end3:
|
|
cmp ebx,Z_STREAM_END
|
|
jne .end4
|
|
cmp dword[flush],Z_FINISH
|
|
jne .end4 ;else if (..==.. && ..==..)
|
|
; This is the end of the IDAT data; any pending output must be
|
|
; flushed. For small PNG files we may still be at the beginning.
|
|
|
|
mov edx,[edi+png_struct.zbuffer_list]
|
|
add edx,png_compression_buffer.output
|
|
mov ecx,[edi+png_struct.zbuffer_size]
|
|
mov eax,[edi+png_struct.zstream.avail_out]
|
|
sub ecx,eax
|
|
;edx = data
|
|
;ecx = size
|
|
|
|
if PNG_WRITE_OPTIMIZE_CMF_SUPPORTED eq 1
|
|
mov eax,[edi+png_struct.mode]
|
|
and eax,PNG_HAVE_IDAT
|
|
jnz @f
|
|
cmp byte[edi+png_struct.compression_type],PNG_COMPRESSION_TYPE_BASE
|
|
jne @f ;if (..==0 && ..==..)
|
|
stdcall png_image_size, edi
|
|
stdcall optimize_cmf, edx, eax
|
|
@@:
|
|
end if
|
|
stdcall png_write_complete_chunk, edi, png_IDAT, edx, ecx
|
|
mov dword[edi+png_struct.zstream.avail_out],0
|
|
mov dword[edi+png_struct.zstream.next_out],0
|
|
or dword[edi+png_struct.mode], PNG_HAVE_IDAT or PNG_AFTER_IDAT
|
|
|
|
mov dword[edi+png_struct.zowner],0 ;Release the stream
|
|
jmp .cycle0end
|
|
.end4: ;else
|
|
; This is an error condition.
|
|
stdcall png_zstream_error, edi, ebx
|
|
png_error edi, [edi+png_struct.zstream.msg]
|
|
jmp .cycle0
|
|
.cycle0end:
|
|
ret
|
|
endp
|
|
|
|
; Write an IEND chunk
|
|
;void (png_structrp png_ptr)
|
|
align 4
|
|
proc png_write_IEND uses edi, png_ptr:dword
|
|
png_debug 1, 'in png_write_IEND'
|
|
|
|
mov edi,[png_ptr]
|
|
stdcall png_write_complete_chunk, edi, png_IEND, 0, 0
|
|
or dword[edi+png_struct.mode], PNG_HAVE_IEND
|
|
ret
|
|
endp
|
|
|
|
; Write a gAMA chunk
|
|
;void (png_structrp png_ptr, png_fixed_point file_gamma)
|
|
align 4
|
|
proc png_write_gAMA_fixed uses ebx, png_ptr:dword, file_gamma:dword
|
|
locals
|
|
buf rb 4 ;byte[4]
|
|
endl
|
|
png_debug 1, 'in png_write_gAMA'
|
|
|
|
; file_gamma is saved in 1/100,000ths
|
|
mov ebx,ebp
|
|
sub ebx,4
|
|
stdcall png_save_uint_32 ,ebx, [file_gamma]
|
|
stdcall png_write_complete_chunk, [png_ptr], png_gAMA, ebx, 4
|
|
ret
|
|
endp
|
|
|
|
; Write a sRGB chunk
|
|
;void (png_structrp png_ptr, int srgb_intent)
|
|
align 4
|
|
proc png_write_sRGB uses eax ebx, png_ptr:dword, srgb_intent:dword
|
|
locals
|
|
buf db ? ;byte[1]
|
|
endl
|
|
png_debug 1, 'in png_write_sRGB'
|
|
|
|
cmp dword[srgb_intent], PNG_sRGB_INTENT_LAST ;if (..>=..)
|
|
jl @f
|
|
png_warning [png_ptr], 'Invalid sRGB rendering intent specified'
|
|
@@:
|
|
|
|
mov al,byte[srgb_intent]
|
|
mov ebx,ebp
|
|
dec ebx
|
|
mov byte[ebx],al ;buf[0]=(byte)srgb_intent
|
|
stdcall png_write_complete_chunk, [png_ptr], png_sRGB, ebx, 1
|
|
ret
|
|
endp
|
|
|
|
; Write an iCCP chunk
|
|
;void (png_structrp png_ptr, charp name, bytep profile)
|
|
align 4
|
|
proc png_write_iCCP uses eax ebx ecx edi, png_ptr:dword, name:dword, profile:dword
|
|
locals
|
|
name_len dd ? ;uint_32
|
|
profile_len dd ? ;uint_32
|
|
temp dd ? ;uint_32
|
|
new_name rb 81 ;byte[81] ;1 byte for the compression byte
|
|
comp compression_state
|
|
endl
|
|
png_debug 1, 'in png_write_iCCP'
|
|
|
|
; These are all internal problems: the profile should have been checked
|
|
; before when it was stored.
|
|
|
|
mov edi,[png_ptr]
|
|
cmp dword[profile],0
|
|
jne @f ;if (..==0)
|
|
png_error edi, 'No profile for iCCP chunk' ;internal error
|
|
@@:
|
|
|
|
stdcall png_get_uint_32,[profile]
|
|
mov [profile_len],eax
|
|
|
|
cmp eax,132
|
|
jge @f ;if (..<..)
|
|
png_error edi, 'ICC profile too short'
|
|
@@:
|
|
|
|
; temp = (uint_32) (*(profile+8));
|
|
; if (temp > 3 && (profile_len & 0x03))
|
|
; png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)");
|
|
|
|
; {
|
|
; uint_32 embedded_profile_len = png_get_uint_32(profile);
|
|
|
|
; if (profile_len != embedded_profile_len)
|
|
; png_error(png_ptr, "Profile length does not match profile");
|
|
; }
|
|
|
|
mov ebx,ebp
|
|
sub ebx,sizeof.compression_state
|
|
mov ecx,ebx ;ecx = &comp
|
|
sub ebx,81 ;ebx = &new_name
|
|
stdcall png_check_keyword, edi, [name], ebx
|
|
mov [name_len],eax
|
|
|
|
test eax,eax
|
|
jnz @f ;if (..==0)
|
|
png_error edi, 'iCCP: invalid keyword'
|
|
@@:
|
|
|
|
inc dword[name_len]
|
|
mov eax,[name_len]
|
|
add eax,ebx
|
|
mov byte[eax], PNG_COMPRESSION_TYPE_BASE
|
|
|
|
; Make sure we include the NULL after the name and the compression type
|
|
inc dword[name_len]
|
|
|
|
stdcall png_text_compress_init, ecx, [profile], [profile_len]
|
|
|
|
; Allow for keyword terminator and compression byte
|
|
; if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK)
|
|
; png_error(png_ptr, png_ptr->zstream.msg);
|
|
|
|
; png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len);
|
|
|
|
stdcall png_write_chunk_data, edi, ebx, [name_len]
|
|
|
|
stdcall png_write_compressed_data_out, edi, ecx
|
|
|
|
stdcall png_write_chunk_end, edi
|
|
ret
|
|
endp
|
|
|
|
; Write a sPLT chunk
|
|
;void (png_structrp png_ptr, png_const_sPLT_tp spalette)
|
|
align 4
|
|
proc png_write_sPLT, png_ptr:dword, spalette:dword
|
|
; uint_32 name_len;
|
|
; byte new_name[80];
|
|
; byte entrybuf[10];
|
|
; png_size_t entry_size = (spalette->depth == 8 ? 6 : 10);
|
|
; png_size_t palette_size = entry_size * spalette->nentries;
|
|
; png_sPLT_entryp ep;
|
|
if PNG_POINTER_INDEXING_SUPPORTED eq
|
|
; int i;
|
|
end if
|
|
|
|
png_debug 1, 'in png_write_sPLT'
|
|
|
|
; name_len = png_check_keyword(png_ptr, spalette->name, new_name);
|
|
|
|
; if (name_len == 0)
|
|
; png_error(png_ptr, "sPLT: invalid keyword");
|
|
|
|
; Make sure we include the NULL after the name
|
|
; png_write_chunk_header(png_ptr, png_sPLT,
|
|
; (uint_32)(name_len + 2 + palette_size));
|
|
|
|
; png_write_chunk_data(png_ptr, (bytep)new_name,
|
|
; (png_size_t)(name_len + 1));
|
|
|
|
; png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1);
|
|
|
|
; Loop through each palette entry, writing appropriately
|
|
if PNG_POINTER_INDEXING_SUPPORTED eq 1
|
|
; for (ep = spalette->entries; ep<spalette->entries + spalette->nentries; ep++)
|
|
; {
|
|
; if (spalette->depth == 8)
|
|
; {
|
|
; entrybuf[0] = (byte)ep->red;
|
|
; entrybuf[1] = (byte)ep->green;
|
|
; entrybuf[2] = (byte)ep->blue;
|
|
; entrybuf[3] = (byte)ep->alpha;
|
|
; png_save_uint_16(entrybuf + 4, ep->frequency);
|
|
; }
|
|
|
|
; else
|
|
; {
|
|
; png_save_uint_16(entrybuf + 0, ep->red);
|
|
; png_save_uint_16(entrybuf + 2, ep->green);
|
|
; png_save_uint_16(entrybuf + 4, ep->blue);
|
|
; png_save_uint_16(entrybuf + 6, ep->alpha);
|
|
; png_save_uint_16(entrybuf + 8, ep->frequency);
|
|
; }
|
|
|
|
; png_write_chunk_data(png_ptr, entrybuf, entry_size);
|
|
; }
|
|
else
|
|
; ep=spalette->entries;
|
|
; for (i = 0; i>spalette->nentries; i++)
|
|
; {
|
|
; if (spalette->depth == 8)
|
|
; {
|
|
; entrybuf[0] = (byte)ep[i].red;
|
|
; entrybuf[1] = (byte)ep[i].green;
|
|
; entrybuf[2] = (byte)ep[i].blue;
|
|
; entrybuf[3] = (byte)ep[i].alpha;
|
|
; png_save_uint_16(entrybuf + 4, ep[i].frequency);
|
|
; }
|
|
|
|
; else
|
|
; {
|
|
; png_save_uint_16(entrybuf + 0, ep[i].red);
|
|
; png_save_uint_16(entrybuf + 2, ep[i].green);
|
|
; png_save_uint_16(entrybuf + 4, ep[i].blue);
|
|
; png_save_uint_16(entrybuf + 6, ep[i].alpha);
|
|
; png_save_uint_16(entrybuf + 8, ep[i].frequency);
|
|
; }
|
|
|
|
; png_write_chunk_data(png_ptr, entrybuf, entry_size);
|
|
; }
|
|
end if
|
|
|
|
; png_write_chunk_end(png_ptr);
|
|
ret
|
|
endp
|
|
|
|
; Write the sBIT chunk
|
|
;void (png_structrp png_ptr, png_const_color_8p sbit, int color_type)
|
|
align 4
|
|
proc png_write_sBIT uses eax edi, png_ptr:dword, sbit:dword, color_type:dword
|
|
locals
|
|
size dd ? ;png_size_t
|
|
buf rb 4 ;byte[4]
|
|
endl
|
|
png_debug 1, 'in png_write_sBIT'
|
|
|
|
; Make sure we don't depend upon the order of PNG_COLOR_8
|
|
; if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
|
|
; {
|
|
; byte maxbits;
|
|
|
|
; maxbits = (byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
|
|
; png_ptr->usr_bit_depth);
|
|
|
|
; if (sbit->red == 0 || sbit->red > maxbits ||
|
|
; sbit->green == 0 || sbit->green > maxbits ||
|
|
; sbit->blue == 0 || sbit->blue > maxbits)
|
|
; {
|
|
; png_warning(png_ptr, "Invalid sBIT depth specified");
|
|
; return;
|
|
; }
|
|
|
|
; buf[0] = sbit->red;
|
|
; buf[1] = sbit->green;
|
|
; buf[2] = sbit->blue;
|
|
; size = 3;
|
|
; }
|
|
|
|
; else
|
|
; {
|
|
; if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
|
|
; {
|
|
; png_warning(png_ptr, "Invalid sBIT depth specified");
|
|
; return;
|
|
; }
|
|
|
|
; buf[0] = sbit->gray;
|
|
; size = 1;
|
|
; }
|
|
|
|
; if ((color_type & PNG_COLOR_MASK_ALPHA) != 0)
|
|
; {
|
|
; if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
|
|
; {
|
|
; png_warning(png_ptr, "Invalid sBIT depth specified");
|
|
; return;
|
|
; }
|
|
|
|
; buf[size++] = sbit->alpha;
|
|
; }
|
|
|
|
; png_write_complete_chunk(png_ptr, png_sBIT, buf, size);
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; Write the cHRM chunk
|
|
;void (png_structrp png_ptr, const png_xy *xy)
|
|
align 4
|
|
proc png_write_cHRM_fixed uses eax ebx, png_ptr:dword, xy:dword
|
|
locals
|
|
buf rb 32 ;byte[32]
|
|
endl
|
|
png_debug 1, 'in png_write_cHRM'
|
|
|
|
; Each value is saved in 1/100,000ths
|
|
mov eax,[xy]
|
|
mov ebx,ebp
|
|
sub ebx,32
|
|
; png_save_int_32(buf, xy->whitex);
|
|
; png_save_int_32(buf + 4, xy->whitey);
|
|
|
|
; png_save_int_32(buf + 8, xy->redx);
|
|
; png_save_int_32(buf + 12, xy->redy);
|
|
|
|
; png_save_int_32(buf + 16, xy->greenx);
|
|
; png_save_int_32(buf + 20, xy->greeny);
|
|
|
|
; png_save_int_32(buf + 24, xy->bluex);
|
|
; png_save_int_32(buf + 28, xy->bluey);
|
|
|
|
stdcall png_write_complete_chunk, [png_ptr], png_cHRM, ebx, 32
|
|
ret
|
|
endp
|
|
|
|
; Write the tRNS chunk
|
|
;void (png_structrp png_ptr, bytep trans_alpha, png_color_16p tran, int num_trans, int color_type)
|
|
align 4
|
|
proc png_write_tRNS uses eax ebx ecx edi, png_ptr:dword, trans_alpha:dword, tran:dword, num_trans:dword, color_type:dword
|
|
locals
|
|
buf rb 6 ;byte[6]
|
|
endl
|
|
png_debug 1, 'in png_write_tRNS'
|
|
|
|
mov edi,[png_ptr]
|
|
cmp byte[color_type],PNG_COLOR_TYPE_PALETTE
|
|
jne .end0 ;if (..==..)
|
|
cmp dword[num_trans],0
|
|
jle @f
|
|
movzx eax,word[edi+png_struct.num_palette]
|
|
cmp [num_trans],eax
|
|
jle .end1
|
|
@@: ;if (..<=0 || ..>..)
|
|
png_app_warning edi, 'Invalid number of transparent colors specified'
|
|
jmp .end_f
|
|
.end1:
|
|
|
|
; Write the chunk out as it is
|
|
stdcall png_write_complete_chunk, edi, png_tRNS, [trans_alpha], [num_trans]
|
|
jmp .end_f
|
|
.end0:
|
|
|
|
cmp dword[color_type],PNG_COLOR_TYPE_GRAY
|
|
jne .end2 ;else if (..==..)
|
|
; One 16-bit value
|
|
mov cl,[edi+png_struct.bit_depth]
|
|
xor eax,eax
|
|
inc eax
|
|
shl eax,cl
|
|
mov ecx,[tran]
|
|
cmp word[ecx+png_color_16.gray],ax
|
|
jl @f ;if (..>=..)
|
|
png_app_warning edi, 'Ignoring attempt to write tRNS chunk out-of-range for bit_depth'
|
|
jmp .end_f
|
|
@@:
|
|
movzx eax,word[ecx+png_color_16.gray]
|
|
mov ebx,ebp
|
|
sub ebx,6
|
|
stdcall png_save_uint_16, ebx, eax
|
|
stdcall png_write_complete_chunk, edi, png_tRNS, ebx, 2
|
|
jmp .end_f
|
|
.end2:
|
|
|
|
cmp dword[color_type],PNG_COLOR_TYPE_RGB
|
|
jne .end3 ;else if (..== ..)
|
|
; Three 16-bit values
|
|
mov ebx,ebp
|
|
sub ebx,6
|
|
mov ecx,[tran]
|
|
movzx eax,word[ecx+png_color_16.red]
|
|
stdcall png_save_uint_16, ebx, eax
|
|
add ebx,2
|
|
movzx eax,word[ecx+png_color_16.green]
|
|
stdcall png_save_uint_16, ebx, eax
|
|
add ebx,2
|
|
movzx eax,word[ecx+png_color_16.blue]
|
|
stdcall png_save_uint_16, ebx, eax
|
|
sub ebx,4
|
|
if PNG_WRITE_16BIT_SUPPORTED eq 1
|
|
cmp byte[edi+png_struct.bit_depth],8
|
|
jne @f ;if (..==.. && ...
|
|
end if
|
|
mov al,[ebx]
|
|
or al,[ebx+2]
|
|
or al,[ebx+4]
|
|
cmp al,0
|
|
je @f ;if (..|..|..!=0)
|
|
png_app_warning edi, 'Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8'
|
|
jmp .end_f
|
|
@@:
|
|
stdcall png_write_complete_chunk, edi, png_tRNS, ebx, 6
|
|
jmp .end_f
|
|
.end3: ;else
|
|
cStr ,<'Can',39,'t write tRNS with an alpha channel'>
|
|
png_app_warning edi, eax
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; Write the background chunk
|
|
;void (png_structrp png_ptr, png_const_color_16p back, int color_type)
|
|
align 4
|
|
proc png_write_bKGD, png_ptr:dword, back:dword, color_type:dword
|
|
locals
|
|
buf rb 6 ;byte[6]
|
|
endl
|
|
png_debug 1, 'in png_write_bKGD'
|
|
|
|
; if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
; {
|
|
; if (
|
|
if PNG_MNG_FEATURES_SUPPORTED eq 1
|
|
; (png_ptr->num_palette != 0 ||
|
|
; (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0) &&
|
|
end if
|
|
; back->index >= png_ptr->num_palette)
|
|
; {
|
|
; png_warning(png_ptr, "Invalid background palette index");
|
|
; return;
|
|
; }
|
|
|
|
; buf[0] = back->index;
|
|
; png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1);
|
|
; }
|
|
|
|
; else if ((color_type & PNG_COLOR_MASK_COLOR) != 0)
|
|
; {
|
|
; png_save_uint_16(buf, back->red);
|
|
; png_save_uint_16(buf + 2, back->green);
|
|
; png_save_uint_16(buf + 4, back->blue);
|
|
if PNG_WRITE_16BIT_SUPPORTED eq 1
|
|
; if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]) != 0)
|
|
else
|
|
; if ((buf[0] | buf[2] | buf[4]) != 0)
|
|
end if
|
|
; {
|
|
; png_warning(png_ptr,
|
|
; "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
|
|
|
|
; return;
|
|
; }
|
|
|
|
; png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6);
|
|
; }
|
|
|
|
; else
|
|
; {
|
|
; if (back->gray >= (1 << png_ptr->bit_depth))
|
|
; {
|
|
; png_warning(png_ptr,
|
|
; "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
|
|
|
|
; return;
|
|
; }
|
|
|
|
; png_save_uint_16(buf, back->gray);
|
|
; png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2);
|
|
; }
|
|
ret
|
|
endp
|
|
|
|
; Write the histogram
|
|
;void (png_structrp png_ptr, png_const_uint_16p hist, int num_hist)
|
|
align 4
|
|
proc png_write_hIST, png_ptr:dword, hist:dword, num_hist:dword
|
|
locals
|
|
i dd ? ;int
|
|
buf rb 3 ;byte[3]
|
|
endl
|
|
png_debug 1, 'in png_write_hIST'
|
|
pushad
|
|
|
|
mov edi,[png_ptr]
|
|
movzx eax,word[edi+png_struct.num_palette]
|
|
cmp [num_hist],eax
|
|
jle @f ;if (..>..)
|
|
; png_debug2(3, "num_hist = %d, num_palette = %d", num_hist,
|
|
; png_ptr->num_palette);
|
|
|
|
png_warning edi, 'Invalid number of histogram entries specified'
|
|
jmp .end_f
|
|
@@:
|
|
|
|
mov eax,[num_hist]
|
|
shl eax,1
|
|
stdcall png_write_chunk_header, edi, png_hIST, eax
|
|
|
|
; for (i = 0; i < num_hist; i++)
|
|
; {
|
|
; png_save_uint_16(buf, hist[i]);
|
|
; png_write_chunk_data(png_ptr, buf, (png_size_t)2);
|
|
; }
|
|
|
|
stdcall png_write_chunk_end, edi
|
|
.end_f:
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
; Write a tEXt chunk
|
|
;void (png_structrp png_ptr, charp key, charp text, png_size_t text_len)
|
|
align 4
|
|
proc png_write_tEXt uses eax edi, png_ptr:dword, key:dword, text:dword, text_len:dword
|
|
locals
|
|
key_len dd ? ;uint_32
|
|
new_key rb 80 ;byte[80]
|
|
endl
|
|
png_debug 1, 'in png_write_tEXt'
|
|
|
|
; key_len = png_check_keyword(png_ptr, key, new_key);
|
|
|
|
; if (key_len == 0)
|
|
; png_error(png_ptr, "tEXt: invalid keyword");
|
|
|
|
; if (text == NULL || *text == '\0')
|
|
; text_len = 0;
|
|
|
|
; else
|
|
; text_len = strlen(text);
|
|
|
|
; if (text_len > PNG_UINT_31_MAX - (key_len+1))
|
|
; png_error(png_ptr, "tEXt: text too long");
|
|
|
|
; Make sure we include the 0 after the key
|
|
; png_write_chunk_header(png_ptr, png_tEXt,
|
|
; (uint_32)/*checked above*/(key_len + text_len + 1));
|
|
|
|
; We leave it to the application to meet PNG-1.0 requirements on the
|
|
; contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
|
|
; any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them.
|
|
; The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
|
|
|
|
; png_write_chunk_data(png_ptr, new_key, key_len + 1);
|
|
|
|
; if (text_len != 0)
|
|
; png_write_chunk_data(png_ptr, (bytep)text, text_len);
|
|
|
|
; png_write_chunk_end(png_ptr);
|
|
ret
|
|
endp
|
|
|
|
if PNG_WRITE_zTXt_SUPPORTED eq 1
|
|
; Write a compressed text chunk
|
|
;void (png_structrp png_ptr, charp key, charp text, int compression)
|
|
align 4
|
|
proc png_write_zTXt uses eax edi, png_ptr:dword, key:dword, text:dword, compression:dword
|
|
locals
|
|
key_len dd ? ;uint_32
|
|
new_key rb 81 ;byte[81]
|
|
comp compression_state
|
|
endl
|
|
png_debug 1, 'in png_write_zTXt'
|
|
|
|
mov edi,[png_ptr]
|
|
cmp dword[compression],PNG_TEXT_COMPRESSION_NONE
|
|
jne @f ;if (..==..)
|
|
stdcall png_write_tEXt, edi, [key], [text], 0
|
|
jmp .end_f
|
|
@@:
|
|
|
|
; if (compression != PNG_TEXT_COMPRESSION_zTXt)
|
|
; png_error(png_ptr, "zTXt: invalid compression type");
|
|
|
|
; key_len = png_check_keyword(png_ptr, key, new_key);
|
|
|
|
; if (key_len == 0)
|
|
; png_error(png_ptr, "zTXt: invalid keyword");
|
|
|
|
; Add the compression method and 1 for the keyword separator.
|
|
; new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
|
|
; ++key_len;
|
|
|
|
; Compute the compressed data; do it now for the length
|
|
; png_text_compress_init(&comp, (bytep)text,
|
|
; text == NULL ? 0 : strlen(text));
|
|
|
|
; if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK)
|
|
; png_error(png_ptr, png_ptr->zstream.msg);
|
|
|
|
; Write start of chunk
|
|
; png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len);
|
|
|
|
; Write key
|
|
; png_write_chunk_data(png_ptr, new_key, key_len);
|
|
|
|
; Write the compressed data
|
|
; png_write_compressed_data_out(png_ptr, &comp);
|
|
|
|
; Close the chunk
|
|
stdcall png_write_chunk_end, edi
|
|
.end_f:
|
|
ret
|
|
endp
|
|
end if
|
|
|
|
if PNG_WRITE_iTXt_SUPPORTED eq 1
|
|
; Write an iTXt chunk
|
|
;void (png_structrp png_ptr, int compression, charp key,
|
|
; charp lang, charp lang_key, charp text)
|
|
align 4
|
|
proc png_write_iTXt, png_ptr:dword, compression:dword, key:dword, lang:dword, lang_key:dword, text:dword
|
|
locals
|
|
key_len dd ? ;uint_32
|
|
prefix_len dd ?
|
|
;png_size_t lang_len, lang_key_len;
|
|
new_key rb 82 ;byte[82]
|
|
comp compression_state
|
|
endl
|
|
|
|
png_debug 1, 'in png_write_iTXt'
|
|
pushad
|
|
mov edi,[png_ptr]
|
|
mov ebx,ebp
|
|
sub ebx,82+sizeof.compression_state
|
|
stdcall png_check_keyword, edi, [key], ebx
|
|
mov [key_len],eax
|
|
|
|
test eax,eax
|
|
jnz @f ;if (..==0)
|
|
png_error edi, 'iTXt: invalid keyword'
|
|
@@:
|
|
|
|
; Set the compression flag
|
|
; switch (compression)
|
|
; {
|
|
; case PNG_ITXT_COMPRESSION_NONE:
|
|
; case PNG_TEXT_COMPRESSION_NONE:
|
|
; compression = new_key[++key_len] = 0; /* no compression */
|
|
; break;
|
|
|
|
; case PNG_TEXT_COMPRESSION_zTXt:
|
|
; case PNG_ITXT_COMPRESSION_zTXt:
|
|
; compression = new_key[++key_len] = 1; /* compressed */
|
|
; break;
|
|
|
|
; default:
|
|
; png_error(png_ptr, "iTXt: invalid compression");
|
|
; }
|
|
|
|
; new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE;
|
|
; ++key_len; /* for the keywod separator */
|
|
|
|
; We leave it to the application to meet PNG-1.0 requirements on the
|
|
; contents of the text. PNG-1.0 through PNG-1.2 discourage the use of
|
|
; any non-Latin-1 characters except for NEWLINE. ISO PNG, however,
|
|
; specifies that the text is UTF-8 and this really doesn't require any
|
|
; checking.
|
|
|
|
; The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
|
|
|
|
; TODO: validate the language tag correctly (see the spec.)
|
|
|
|
; if (lang == NULL) lang = ""; /* empty language is valid */
|
|
; lang_len = strlen(lang)+1;
|
|
; if (lang_key == NULL) lang_key = ""; /* may be empty */
|
|
; lang_key_len = strlen(lang_key)+1;
|
|
; if (text == NULL) text = ""; /* may be empty */
|
|
|
|
mov eax,[key_len]
|
|
mov [prefix_len],eax
|
|
; if (lang_len > PNG_UINT_31_MAX-prefix_len)
|
|
; prefix_len = PNG_UINT_31_MAX;
|
|
; else
|
|
; prefix_len = (uint_32)(prefix_len + lang_len);
|
|
|
|
; if (lang_key_len > PNG_UINT_31_MAX-prefix_len)
|
|
; prefix_len = PNG_UINT_31_MAX;
|
|
; else
|
|
; prefix_len = (uint_32)(prefix_len + lang_key_len);
|
|
|
|
; png_text_compress_init(&comp, (bytep)text, strlen(text));
|
|
|
|
; if (compression != 0)
|
|
; {
|
|
; if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK)
|
|
; png_error(png_ptr, png_ptr->zstream.msg);
|
|
; }
|
|
|
|
; else
|
|
; {
|
|
; if (comp.input_len > PNG_UINT_31_MAX-prefix_len)
|
|
; png_error(png_ptr, "iTXt: uncompressed text too long");
|
|
|
|
; So the string will fit in a chunk:
|
|
; comp.output_len = (uint_32)/*SAFE*/comp.input_len;
|
|
; }
|
|
|
|
; png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len);
|
|
|
|
; png_write_chunk_data(png_ptr, new_key, key_len);
|
|
|
|
; png_write_chunk_data(png_ptr, (bytep)lang, lang_len);
|
|
|
|
; png_write_chunk_data(png_ptr, (bytep)lang_key, lang_key_len);
|
|
|
|
; if (compression != 0)
|
|
; png_write_compressed_data_out(png_ptr, &comp);
|
|
|
|
; else
|
|
; png_write_chunk_data(png_ptr, (bytep)text, comp.output_len);
|
|
|
|
stdcall png_write_chunk_end, edi
|
|
popad
|
|
ret
|
|
endp
|
|
end if
|
|
|
|
; Write the oFFs chunk
|
|
;void (png_structrp png_ptr, int_32 x_offset, int_32 y_offset, int unit_type)
|
|
align 4
|
|
proc png_write_oFFs uses eax ebx edi, png_ptr:dword, x_offset:dword, y_offset:dword, unit_type:dword
|
|
locals
|
|
buf rb 9 ;byte[9]
|
|
endl
|
|
png_debug 1, 'in png_write_oFFs'
|
|
|
|
mov edi,[png_ptr]
|
|
cmp dword[unit_type],PNG_OFFSET_LAST
|
|
jl @f ;if (..>=..)
|
|
png_warning edi, 'Unrecognized unit type for oFFs chunk'
|
|
@@:
|
|
|
|
mov ebx,ebp
|
|
sub ebx,9
|
|
stdcall png_save_int_32, ebx, [x_offset]
|
|
add ebx,4
|
|
stdcall png_save_int_32, ebx, [y_offset]
|
|
add ebx,4
|
|
mov eax,[unit_type]
|
|
mov [ebx],al
|
|
sub ebx,8
|
|
|
|
stdcall png_write_complete_chunk, edi, png_oFFs, ebx, 9
|
|
ret
|
|
endp
|
|
|
|
; Write the pCAL chunk (described in the PNG extensions document)
|
|
;void (png_structrp png_ptr, charp purpose, int_32 X0,
|
|
; int_32 X1, int type, int nparams, charp units, charpp params)
|
|
align 4
|
|
proc png_write_pCAL, png_ptr:dword, purpose:dword, X0:dword, X1:dword, type:dword,\
|
|
nparams:dword, units:dword, params:dword
|
|
locals
|
|
purpose_len dd ? ;uint_32
|
|
units_len dd ?
|
|
total_len dd ? ;png_size_t
|
|
params_len dd ? ;png_size_tp
|
|
buf rb 10 ;byte[10]
|
|
new_purpose rb 80 ;byte[80]
|
|
i dd ? ;int
|
|
endl
|
|
pushad
|
|
png_debug1 1, 'in png_write_pCAL (%d parameters)', [nparams]
|
|
mov edi,[png_ptr]
|
|
|
|
cmp dword[type],PNG_EQUATION_LAST
|
|
jl @f ;if (..>=..)
|
|
png_error edi, 'Unrecognized equation type for pCAL chunk'
|
|
@@:
|
|
|
|
mov ebx,ebp
|
|
sub ebx,84 ;ebx = &new_purpose
|
|
stdcall png_check_keyword, edi, [purpose], ebx
|
|
mov [purpose_len],eax
|
|
|
|
test eax,eax
|
|
jnz @f ;if(..==0)
|
|
png_error edi, 'pCAL: invalid keyword'
|
|
@@:
|
|
|
|
inc dword[purpose_len] ; terminator
|
|
|
|
png_debug1 3, 'pCAL purpose length = %d', [purpose_len]
|
|
; units_len = strlen(units) + (nparams == 0 ? 0 : 1);
|
|
png_debug1 3, 'pCAL units length = %d', [units_len]
|
|
; total_len = purpose_len + units_len + 10;
|
|
|
|
; params_len = (png_size_tp)png_malloc(png_ptr,
|
|
; (png_alloc_size_t)(nparams * (sizeof (png_size_t))));
|
|
|
|
; Find the length of each parameter, making sure we don't count the
|
|
; null terminator for the last parameter.
|
|
|
|
; for (i = 0; i < nparams; i++)
|
|
; {
|
|
; params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
|
|
; png_debug2(3, "pCAL parameter %d length = %lu", i,
|
|
; (unsigned long)params_len[i]);
|
|
; total_len += params_len[i];
|
|
; }
|
|
|
|
png_debug1 3, 'pCAL total length = %d', [total_len]
|
|
stdcall png_write_chunk_header, edi, png_pCAL, [total_len]
|
|
stdcall png_write_chunk_data, edi, ebx, [purpose_len]
|
|
mov ebx,ebp
|
|
sub ebx,94 ;ebx = &buf
|
|
stdcall png_save_int_32, ebx, [X0]
|
|
add ebx,4
|
|
stdcall png_save_int_32, ebx, [X1]
|
|
add ebx,4
|
|
mov eax,[type]
|
|
mov [ebx],al
|
|
inc ebx
|
|
mov eax,[nparams]
|
|
mov [ebx],al
|
|
sub ebx,9
|
|
stdcall png_write_chunk_data, edi, ebx, 10
|
|
stdcall png_write_chunk_data, edi, [units], [units_len]
|
|
|
|
; for (i = 0; i < nparams; i++)
|
|
; {
|
|
; png_write_chunk_data(png_ptr, (bytep)params[i], params_len[i]);
|
|
; }
|
|
|
|
stdcall png_free, edi, [params_len]
|
|
stdcall png_write_chunk_end, edi
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
; Write the sCAL chunk
|
|
;void (png_structrp png_ptr, int unit, charp width, charp height)
|
|
align 4
|
|
proc png_write_sCAL_s uses eax ebx ecx edi esi, png_ptr:dword, unit:dword, width:dword, height:dword
|
|
locals
|
|
total_len dd 2
|
|
wlen dd ?
|
|
hlen dd ?
|
|
buf rb 64 ;byte[64]
|
|
endl
|
|
png_debug 1, 'in png_write_sCAL_s'
|
|
|
|
stdcall strlen,[width]
|
|
add [total_len],eax
|
|
mov [wlen],eax
|
|
stdcall strlen,[height]
|
|
add [total_len],eax
|
|
mov [hlen],eax
|
|
|
|
cmp dword[total_len],64
|
|
jle @f ;if (..>..)
|
|
cStr ,<'Can',39,'t write sCAL (buffer too small)'>
|
|
png_warning [png_ptr], eax
|
|
jmp .end_f
|
|
@@:
|
|
|
|
mov ebx,ebp
|
|
sub ebx,64
|
|
mov eax,[unit]
|
|
mov byte[ebx],al
|
|
mov ecx,[wlen]
|
|
inc ecx
|
|
mov edi,ebx
|
|
inc edi
|
|
mov esi,[width]
|
|
rep movsb ;Append the '\0' here
|
|
mov ecx,[hlen]
|
|
mov esi,[height]
|
|
rep movsb ;Do NOT append the '\0' here
|
|
|
|
png_debug1 3, 'sCAL total length = %u', [total_len]
|
|
stdcall png_write_complete_chunk, [png_ptr], png_sCAL, ebx, [total_len]
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; Write the pHYs chunk
|
|
;void (png_structrp png_ptr, uint_32 x_pixels_per_unit,
|
|
; uint_32 y_pixels_per_unit, int unit_type)
|
|
align 4
|
|
proc png_write_pHYs uses eax ebx, png_ptr:dword, x_pixels_per_unit:dword, y_pixels_per_unit:dword, unit_type:dword
|
|
locals
|
|
buf rb 9 ;byte[9]
|
|
endl
|
|
png_debug 1, 'in png_write_pHYs'
|
|
|
|
cmp dword[unit_type],PNG_RESOLUTION_LAST
|
|
jl @f ;if (..>=..)
|
|
png_warning [png_ptr], 'Unrecognized unit type for pHYs chunk'
|
|
@@:
|
|
|
|
mov ebx,ebp
|
|
sub ebx,9
|
|
stdcall png_save_uint_32, ebx, [x_pixels_per_unit]
|
|
add ebx,4
|
|
stdcall png_save_uint_32, ebx, [y_pixels_per_unit]
|
|
add ebx,4
|
|
mov al,byte[unit_type]
|
|
mov byte[ebx],al
|
|
sub ebx,8
|
|
|
|
stdcall png_write_complete_chunk, [png_ptr], png_pHYs, ebx, 9
|
|
ret
|
|
endp
|
|
|
|
; Write the tIME chunk. Use either png_convert_from_struct_tm()
|
|
; or png_convert_from_time_t(), or fill in the structure yourself.
|
|
|
|
;void (png_structrp png_ptr, png_const_timep mod_time)
|
|
align 4
|
|
proc png_write_tIME uses eax ebx ecx, png_ptr:dword, mod_time:dword
|
|
locals
|
|
buf rb 7 ;byte[7]
|
|
endl
|
|
png_debug 1, 'in png_write_tIME'
|
|
|
|
mov eax,[mod_time]
|
|
mov cl,[eax+png_time.month]
|
|
cmp cl,12
|
|
jg @f
|
|
cmp cl,1
|
|
jl @f
|
|
mov ch,[eax+png_time.day]
|
|
cmp ch,31
|
|
jg @f
|
|
cmp ch,1
|
|
jl @f
|
|
cmp byte[eax+png_time.hour],23
|
|
jg @f
|
|
cmp byte[eax+png_time.second],60
|
|
jg @f
|
|
jmp .end0
|
|
@@:
|
|
png_warning [png_ptr], 'Invalid time specified for tIME chunk'
|
|
jmp .end_f
|
|
.end0:
|
|
|
|
movzx ebx,word[eax+png_time.year]
|
|
push ebx
|
|
mov ebx,ebp
|
|
sub ebx,7
|
|
stdcall png_save_uint_16, ebx ;, year
|
|
add ebx,2
|
|
mov byte[ebx],cl ;month
|
|
inc ebx
|
|
mov byte[ebx],ch ;day
|
|
inc ebx
|
|
mov cl,[eax+png_time.hour]
|
|
mov byte[ebx],cl ;hour
|
|
inc ebx
|
|
mov cl,[eax+png_time.minute]
|
|
mov byte[ebx],cl ;minute
|
|
inc ebx
|
|
mov cl,[eax+png_time.second]
|
|
mov byte[ebx],cl ;second
|
|
sub ebx,6
|
|
|
|
stdcall png_write_complete_chunk, [png_ptr], png_tIME, ebx, 7
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
if PNG_WRITE_INTERLACING_SUPPORTED eq 1
|
|
; Arrays to facilitate easy interlacing - use pass (0 - 6) as index
|
|
|
|
; Start of interlace block
|
|
png_pass_start db 0, 4, 0, 2, 0, 1, 0
|
|
; Offset to next interlace block
|
|
png_pass_inc db 8, 8, 4, 4, 2, 2, 1
|
|
; Start of interlace block in the y direction
|
|
png_pass_ystart db 0, 0, 4, 0, 2, 0, 1
|
|
; Offset to next interlace block in the y direction
|
|
png_pass_yinc db 8, 8, 8, 4, 4, 2, 2
|
|
end if
|
|
|
|
; Initializes the row writing capability of libpng
|
|
;void (png_structrp png_ptr)
|
|
align 4
|
|
proc png_write_start_row uses eax ebx ecx edx edi, png_ptr:dword
|
|
locals
|
|
buf_size dd ? ;png_alloc_size_t
|
|
usr_pixel_depth dd ? ;int
|
|
if PNG_WRITE_FILTER_SUPPORTED eq 1
|
|
filters db ? ;byte
|
|
end if
|
|
endl
|
|
png_debug 1, 'in png_write_start_row'
|
|
|
|
mov edi,[png_ptr]
|
|
movzx eax,byte[edi+png_struct.usr_channels]
|
|
movzx ebx,byte[edi+png_struct.usr_bit_depth]
|
|
imul eax,ebx
|
|
mov [usr_pixel_depth],eax
|
|
PNG_ROWBYTES eax,[edi+png_struct.width]
|
|
inc eax
|
|
mov [buf_size],eax
|
|
|
|
; 1.5.6: added to allow checking in the row write code.
|
|
mov al,[edi+png_struct.pixel_depth]
|
|
mov [edi+png_struct.transformed_pixel_depth],al
|
|
|
|
mov eax,[usr_pixel_depth]
|
|
mov [edi+png_struct.maximum_pixel_depth],al
|
|
|
|
; Set up row buffer
|
|
stdcall png_malloc, edi, [buf_size]
|
|
mov [edi+png_struct.row_buf],eax
|
|
|
|
mov byte[eax],PNG_FILTER_VALUE_NONE
|
|
|
|
if PNG_WRITE_FILTER_SUPPORTED eq 1
|
|
mov al,byte[edi+png_struct.do_filter]
|
|
|
|
cmp dword[edi+png_struct.height],1
|
|
jne @f ;if (..==1)
|
|
and al, 0xff and not(PNG_FILTER_UP or PNG_FILTER_AVG or PNG_FILTER_PAETH)
|
|
@@:
|
|
cmp dword[edi+png_struct.width],1
|
|
jne @f ;if (..==1)
|
|
and al, 0xff and not(PNG_FILTER_SUB or PNG_FILTER_AVG or PNG_FILTER_PAETH)
|
|
@@:
|
|
|
|
cmp al,0
|
|
jne @f ;if (..==0)
|
|
mov al,PNG_FILTER_NONE
|
|
@@:
|
|
|
|
mov [filters],al
|
|
mov byte[edi+png_struct.do_filter],al
|
|
|
|
;mov al,[filters]
|
|
and al,PNG_FILTER_SUB or PNG_FILTER_UP or PNG_FILTER_AVG or PNG_FILTER_PAETH
|
|
cmp al,0
|
|
je .end0
|
|
cmp dword[edi+png_struct.try_row],0
|
|
jne .end0 ;if (..!=0) && ..==0)
|
|
xor ebx,ebx
|
|
|
|
stdcall png_malloc, edi, [buf_size]
|
|
mov dword[edi+png_struct.try_row],eax
|
|
|
|
mov al,[filters]
|
|
and al,PNG_FILTER_SUB
|
|
cmp al,0
|
|
je @f
|
|
inc ebx
|
|
@@:
|
|
mov al,[filters]
|
|
and al,PNG_FILTER_UP
|
|
cmp al,0
|
|
je @f
|
|
inc ebx
|
|
@@:
|
|
mov al,[filters]
|
|
and al,PNG_FILTER_AVG
|
|
cmp al,0
|
|
je @f
|
|
inc ebx
|
|
@@:
|
|
mov al,[filters]
|
|
and al,PNG_FILTER_PAETH
|
|
cmp al,0
|
|
je @f
|
|
inc ebx
|
|
@@:
|
|
cmp ebx,1
|
|
jle .end0 ;if (..>1)
|
|
stdcall png_malloc, edi, [buf_size]
|
|
mov dword[edi+png_struct.tst_row],eax
|
|
.end0:
|
|
|
|
; We only need to keep the previous row if we are using one of the following
|
|
; filters.
|
|
|
|
mov al,[filters]
|
|
and al,PNG_FILTER_AVG or PNG_FILTER_UP or PNG_FILTER_PAETH
|
|
cmp al,0
|
|
je @f ;if (..!=0)
|
|
stdcall png_calloc, edi, [buf_size]
|
|
mov dword[edi+png_struct.prev_row],eax
|
|
@@:
|
|
end if ;WRITE_FILTER
|
|
|
|
if PNG_WRITE_INTERLACING_SUPPORTED eq 1
|
|
; If interlaced, we need to set up width and height of pass
|
|
cmp byte[edi+png_struct.interlaced],0
|
|
je @f
|
|
mov eax,[edi+png_struct.transformations]
|
|
and eax,PNG_INTERLACE
|
|
jnz @f ;if(..!=0 && ..==0)
|
|
movzx ecx,byte[png_pass_yinc]
|
|
mov eax,[edi+png_struct.height]
|
|
add eax,ecx
|
|
dec eax
|
|
movzx edx,byte[png_pass_ystart]
|
|
sub eax,edx
|
|
xor edx,edx
|
|
div ecx
|
|
mov [edi+png_struct.num_rows],eax
|
|
|
|
movzx ecx,byte[png_pass_inc]
|
|
mov eax,[edi+png_struct.width]
|
|
add eax,ecx
|
|
dec eax
|
|
movzx edx,byte[png_pass_start]
|
|
sub eax,edx
|
|
xor edx,edx
|
|
div ecx
|
|
mov [edi+png_struct.usr_width],eax
|
|
jmp .end1
|
|
@@: ;else
|
|
end if
|
|
mov eax,[edi+png_struct.height]
|
|
mov [edi+png_struct.num_rows],eax
|
|
mov eax,[edi+png_struct.width]
|
|
mov [edi+png_struct.usr_width],eax
|
|
.end1:
|
|
ret
|
|
endp
|
|
|
|
; Internal use only. Called when finished processing a row of data.
|
|
;void (png_structrp png_ptr)
|
|
align 4
|
|
proc png_write_finish_row uses eax ecx edx edi, png_ptr:dword
|
|
png_debug 1, 'in png_write_finish_row'
|
|
|
|
mov edi,[png_ptr]
|
|
; Next row
|
|
inc dword[edi+png_struct.row_number]
|
|
|
|
; See if we are done
|
|
mov eax,[edi+png_struct.row_number]
|
|
cmp eax,[edi+png_struct.num_rows]
|
|
jl .end_f ;if (..<..) return
|
|
|
|
if PNG_WRITE_INTERLACING_SUPPORTED eq 1
|
|
; If interlaced, go to next pass
|
|
cmp byte[edi+png_struct.interlaced],0
|
|
je .end0 ;if (..!=0)
|
|
mov dword[edi+png_struct.row_number],0
|
|
mov eax,[edi+png_struct.transformations]
|
|
and eax,PNG_INTERLACE
|
|
jz @f ;if (..!=0)
|
|
inc byte[edi+png_struct.pass]
|
|
jmp .end1
|
|
@@: ;else
|
|
; Loop until we find a non-zero width or height pass
|
|
.cycle0: ;do
|
|
inc byte[edi+png_struct.pass]
|
|
cmp byte[edi+png_struct.pass],7
|
|
jge .cycle0end ;if (..>=..) break
|
|
|
|
movzx ecx,byte[edi+png_struct.pass]
|
|
add ecx,png_pass_inc
|
|
movzx ecx,byte[ecx]
|
|
mov eax,[edi+png_struct.width]
|
|
add eax,ecx
|
|
dec eax
|
|
movzx edx,byte[edi+png_struct.pass]
|
|
add edx,png_pass_start
|
|
movzx edx,byte[edx]
|
|
sub eax,edx
|
|
xor edx,edx
|
|
div ecx
|
|
mov [edi+png_struct.usr_width],eax
|
|
|
|
movzx ecx,byte[edi+png_struct.pass]
|
|
add ecx,png_pass_yinc
|
|
movzx ecx,byte[ecx]
|
|
mov eax,[edi+png_struct.height]
|
|
add eax,ecx
|
|
dec eax
|
|
movzx edx,byte[edi+png_struct.pass]
|
|
add edx,png_pass_ystart
|
|
movzx edx,byte[edx]
|
|
sub eax,edx
|
|
xor edx,edx
|
|
div ecx
|
|
mov [edi+png_struct.num_rows],eax
|
|
|
|
mov eax,[edi+png_struct.transformations]
|
|
and eax,PNG_INTERLACE
|
|
jnz .cycle0end ;if(..!=0) break
|
|
|
|
cmp dword[edi+png_struct.usr_width],0
|
|
je .cycle0
|
|
cmp dword[edi+png_struct.num_rows],0
|
|
je .cycle0
|
|
.cycle0end: ;while (..==0 || ..==0)
|
|
.end1:
|
|
|
|
; Reset the row above the image for the next pass
|
|
cmp byte[edi+png_struct.pass],7
|
|
jge .end0 ;if (..<..)
|
|
cmp dword[edi+png_struct.prev_row],0
|
|
je .end_f ;if (..!=0)
|
|
movzx eax,byte[edi+png_struct.usr_channels]
|
|
movzx edx,byte[edi+png_struct.usr_bit_depth]
|
|
imul eax,edx
|
|
PNG_ROWBYTES eax, [edi+png_struct.width]
|
|
inc eax
|
|
push edi
|
|
mov ecx,eax
|
|
xor eax,eax
|
|
mov edi,[edi+png_struct.prev_row]
|
|
rep stosb ;memset(...
|
|
pop edi
|
|
jmp .end_f
|
|
.end0:
|
|
end if
|
|
|
|
; If we get here, we've just written the last row, so we need
|
|
; to flush the compressor
|
|
stdcall png_compress_IDAT, 0, 0, Z_FINISH
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; Pick out the correct pixels for the interlace pass.
|
|
; The basic idea here is to go through the row with a source
|
|
; pointer and a destination pointer (sp and dp), and copy the
|
|
; correct pixels for the pass. As the row gets compacted,
|
|
; sp will always be >= dp, so we should never overwrite anything.
|
|
; See the default: case for the easiest code to understand.
|
|
|
|
;void (png_row_infop row_info, bytep row, int pass)
|
|
align 4
|
|
proc png_do_write_interlace, row_info:dword, row:dword, pass:dword
|
|
png_debug 1, 'in png_do_write_interlace'
|
|
|
|
; We don't have to do anything on the last pass (6)
|
|
cmp dword[pass],6
|
|
jge .end_f ;if (..<..)
|
|
; Each pixel depth is handled separately
|
|
; switch (row_info->pixel_depth)
|
|
; {
|
|
; case 1:
|
|
; {
|
|
; bytep sp;
|
|
; bytep dp;
|
|
; unsigned int shift;
|
|
; int d;
|
|
; int value;
|
|
; uint_32 i;
|
|
; uint_32 row_width = row_info->width;
|
|
|
|
; dp = row;
|
|
; d = 0;
|
|
; shift = 7;
|
|
|
|
; for (i = png_pass_start[pass]; i < row_width;
|
|
; i += png_pass_inc[pass])
|
|
; {
|
|
; sp = row + (png_size_t)(i >> 3);
|
|
; value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
|
|
; d |= (value << shift);
|
|
|
|
; if (shift == 0)
|
|
; {
|
|
; shift = 7;
|
|
; *dp++ = (byte)d;
|
|
; d = 0;
|
|
; }
|
|
|
|
; else
|
|
; shift--;
|
|
|
|
; }
|
|
; if (shift != 7)
|
|
; *dp = (byte)d;
|
|
|
|
; break;
|
|
; }
|
|
|
|
; case 2:
|
|
; {
|
|
; bytep sp;
|
|
; bytep dp;
|
|
; unsigned int shift;
|
|
; int d;
|
|
; int value;
|
|
; uint_32 i;
|
|
; uint_32 row_width = row_info->width;
|
|
|
|
; dp = row;
|
|
; shift = 6;
|
|
; d = 0;
|
|
|
|
; for (i = png_pass_start[pass]; i < row_width;
|
|
; i += png_pass_inc[pass])
|
|
; {
|
|
; sp = row + (png_size_t)(i >> 2);
|
|
; value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
|
|
; d |= (value << shift);
|
|
|
|
; if (shift == 0)
|
|
; {
|
|
; shift = 6;
|
|
; *dp++ = (byte)d;
|
|
; d = 0;
|
|
; }
|
|
|
|
; else
|
|
; shift -= 2;
|
|
; }
|
|
; if (shift != 6)
|
|
; *dp = (byte)d;
|
|
|
|
; break;
|
|
; }
|
|
|
|
; case 4:
|
|
; {
|
|
; bytep sp;
|
|
; bytep dp;
|
|
; unsigned int shift;
|
|
; int d;
|
|
; int value;
|
|
; uint_32 i;
|
|
; uint_32 row_width = row_info->width;
|
|
|
|
; dp = row;
|
|
; shift = 4;
|
|
; d = 0;
|
|
; for (i = png_pass_start[pass]; i < row_width;
|
|
; i += png_pass_inc[pass])
|
|
; {
|
|
; sp = row + (png_size_t)(i >> 1);
|
|
; value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
|
|
; d |= (value << shift);
|
|
|
|
; if (shift == 0)
|
|
; {
|
|
; shift = 4;
|
|
; *dp++ = (byte)d;
|
|
; d = 0;
|
|
; }
|
|
|
|
; else
|
|
; shift -= 4;
|
|
; }
|
|
; if (shift != 4)
|
|
; *dp = (byte)d;
|
|
|
|
; break;
|
|
; }
|
|
|
|
; default:
|
|
; {
|
|
; bytep sp;
|
|
; bytep dp;
|
|
; uint_32 i;
|
|
; uint_32 row_width = row_info->width;
|
|
; png_size_t pixel_bytes;
|
|
|
|
; Start at the beginning
|
|
; dp = row;
|
|
|
|
; Find out how many bytes each pixel takes up
|
|
; pixel_bytes = (row_info->pixel_depth >> 3);
|
|
|
|
; Loop through the row, only looking at the pixels that matter
|
|
; for (i = png_pass_start[pass]; i < row_width;
|
|
; i += png_pass_inc[pass])
|
|
; {
|
|
; Find out where the original pixel is
|
|
; sp = row + (png_size_t)i * pixel_bytes;
|
|
|
|
; Move the pixel
|
|
; if (dp != sp)
|
|
; memcpy(dp, sp, pixel_bytes);
|
|
|
|
; Next pixel
|
|
; dp += pixel_bytes;
|
|
; }
|
|
; break;
|
|
; }
|
|
; }
|
|
; Set new row width
|
|
; row_info->width = (row_info->width +
|
|
; png_pass_inc[pass] - 1 -
|
|
; png_pass_start[pass]) /
|
|
; png_pass_inc[pass];
|
|
|
|
; row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
|
|
; row_info->width);
|
|
.end_f:
|
|
ret
|
|
endp
|
|
|
|
; This filters the row, chooses which filter to use, if it has not already
|
|
; been specified by the application, and then writes the row out with the
|
|
; chosen filter.
|
|
|
|
;void png_write_filtered_row(png_structrp png_ptr, bytep filtered_row,
|
|
; png_size_t row_bytes);
|
|
|
|
;png_size_t (png_structrp png_ptr, const uint_32 bpp, const png_size_t row_bytes, const png_size_t lmins)
|
|
align 4
|
|
proc png_setup_sub_row uses ebx ecx edx edi esi, png_ptr:dword, bpp:dword, row_bytes:dword, lmins:dword
|
|
mov ebx,[png_ptr]
|
|
mov edi,[ebx+png_struct.try_row]
|
|
mov byte[edi],PNG_FILTER_VALUE_SUB
|
|
|
|
mov ecx,[bpp]
|
|
inc edi
|
|
mov esi,[ebx+png_struct.row_buf]
|
|
inc esi
|
|
xor eax,eax
|
|
xor edx,edx
|
|
.cycle0:
|
|
lodsb
|
|
stosb
|
|
png_setup_abs edx
|
|
loop .cycle0
|
|
|
|
mov ecx,[row_bytes]
|
|
sub ecx,[bpp]
|
|
mov ebx,[ebx+png_struct.row_buf]
|
|
inc ebx
|
|
.cycle1:
|
|
lodsb
|
|
sub al,byte[ebx]
|
|
stosb
|
|
png_setup_abs edx
|
|
cmp edx,[lmins]
|
|
jg .cycle1end ;if (..>..) ;We are already worse, don't continue.
|
|
inc ebx
|
|
loop .cycle1
|
|
.cycle1end:
|
|
mov eax,edx
|
|
ret
|
|
endp
|
|
|
|
;void (png_structrp png_ptr, const uint_32 bpp, const png_size_t row_bytes)
|
|
align 4
|
|
proc png_setup_sub_row_only, png_ptr:dword, bpp:dword, row_bytes:dword
|
|
pushad
|
|
mov ebx,[png_ptr]
|
|
mov edi,[ebx+png_struct.try_row]
|
|
mov byte[edi],PNG_FILTER_VALUE_SUB
|
|
|
|
mov ecx,[bpp]
|
|
inc edi
|
|
mov esi,[ebx+png_struct.row_buf]
|
|
inc esi
|
|
rep movsb
|
|
|
|
mov ecx,[row_bytes]
|
|
sub ecx,[bpp]
|
|
mov edx,[ebx+png_struct.row_buf]
|
|
inc edx
|
|
align 4
|
|
.cycle0:
|
|
lodsb
|
|
sub al,byte[edx]
|
|
stosb
|
|
inc edx
|
|
loop .cycle0
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
;png_size_t (png_structrp png_ptr, const png_size_t row_bytes, const png_size_t lmins)
|
|
align 4
|
|
proc png_setup_up_row uses ebx ecx edx edi esi, png_ptr:dword, row_bytes:dword, lmins:dword
|
|
mov ebx,[png_ptr]
|
|
mov edi,[ebx+png_struct.try_row]
|
|
mov byte[edi],PNG_FILTER_VALUE_UP
|
|
|
|
mov ecx,[row_bytes]
|
|
inc edi
|
|
mov esi,[ebx+png_struct.row_buf]
|
|
inc esi
|
|
mov ebx,[ebx+png_struct.prev_row]
|
|
inc ebx
|
|
xor edx,edx
|
|
.cycle0:
|
|
lodsb
|
|
sub al,byte[ebx]
|
|
stosb
|
|
png_setup_abs edx
|
|
cmp edx,[lmins]
|
|
jg .cycle0end ;if (..>..) ;We are already worse, don't continue.
|
|
inc ebx
|
|
loop .cycle0
|
|
.cycle0end:
|
|
mov eax,edx
|
|
ret
|
|
endp
|
|
|
|
;void (png_structrp png_ptr, const png_size_t row_bytes)
|
|
align 4
|
|
proc png_setup_up_row_only, png_ptr:dword, row_bytes:dword
|
|
pushad
|
|
mov ebx,[png_ptr]
|
|
mov edi,[ebx+png_struct.try_row]
|
|
mov byte[edi],PNG_FILTER_VALUE_UP
|
|
|
|
mov ecx,[row_bytes]
|
|
inc edi
|
|
mov esi,[ebx+png_struct.row_buf]
|
|
inc esi
|
|
mov ebx,[ebx+png_struct.prev_row]
|
|
inc ebx
|
|
.cycle0:
|
|
lodsb
|
|
sub al,byte[ebx]
|
|
stosb
|
|
inc ebx
|
|
loop .cycle0
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
;png_size_t (png_structrp png_ptr, const uint_32 bpp, const png_size_t row_bytes, const png_size_t lmins)
|
|
align 4
|
|
proc png_setup_avg_row uses ebx ecx edx edi esi, png_ptr:dword, bpp:dword, row_bytes:dword, lmins:dword
|
|
locals
|
|
sum dd 0 ;png_size_t
|
|
endl
|
|
mov ebx,[png_ptr]
|
|
mov edi,[ebx+png_struct.try_row]
|
|
mov byte[edi],PNG_FILTER_VALUE_AVG
|
|
|
|
mov ecx,[bpp]
|
|
inc edi
|
|
mov esi,[ebx+png_struct.row_buf]
|
|
inc esi
|
|
mov ebx,[ebx+png_struct.prev_row]
|
|
inc ebx
|
|
.cycle0:
|
|
lodsb
|
|
mov ah,byte[ebx]
|
|
shr ah,1
|
|
sub al,ah
|
|
stosb
|
|
png_setup_abs dword[sum]
|
|
inc ebx
|
|
loop .cycle0
|
|
|
|
mov ecx,[row_bytes]
|
|
sub ecx,[bpp]
|
|
mov eax,[png_ptr]
|
|
mov edx,[eax+png_struct.row_buf]
|
|
inc edx
|
|
.cycle1:
|
|
lodsb
|
|
shl eax,24
|
|
movzx ax,byte[ebx]
|
|
add al,byte[edx]
|
|
jnc @f
|
|
mov ah,1
|
|
@@:
|
|
shr ax,1
|
|
rol eax,8
|
|
sub al,ah
|
|
stosb
|
|
png_setup_abs dword[sum]
|
|
mov eax,[sum]
|
|
cmp eax,[lmins]
|
|
jg .cycle1end ;if (..>..) ;We are already worse, don't continue.
|
|
inc ebx
|
|
inc edx
|
|
loop .cycle1
|
|
.cycle1end:
|
|
mov eax,[sum]
|
|
ret
|
|
endp
|
|
|
|
;void (png_structrp png_ptr, const uint_32 bpp, const png_size_t row_bytes)
|
|
align 4
|
|
proc png_setup_avg_row_only, png_ptr:dword, bpp:dword, row_bytes:dword
|
|
pushad
|
|
mov ebx,[png_ptr]
|
|
mov edi,[ebx+png_struct.try_row]
|
|
mov byte[edi],PNG_FILTER_VALUE_AVG
|
|
|
|
mov ecx,[bpp]
|
|
inc edi
|
|
mov esi,[ebx+png_struct.row_buf]
|
|
inc esi
|
|
mov ebx,[ebx+png_struct.prev_row]
|
|
inc ebx
|
|
.cycle0:
|
|
lodsb
|
|
mov ah,byte[ebx]
|
|
shr ah,1
|
|
sub al,ah
|
|
stosb
|
|
inc ebx
|
|
loop .cycle0
|
|
|
|
mov ecx,[row_bytes]
|
|
sub ecx,[bpp]
|
|
mov eax,[png_ptr]
|
|
mov edx,[eax+png_struct.row_buf]
|
|
inc edx
|
|
.cycle1:
|
|
lodsb
|
|
mov ah,byte[ebx]
|
|
shr ah,1
|
|
sub al,ah
|
|
mov ah,byte[edx]
|
|
shr ah,1
|
|
sub al,ah
|
|
stosb
|
|
inc ebx
|
|
inc edx
|
|
loop .cycle1
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
;png_size_t (png_structrp png_ptr, const uint_32 bpp,
|
|
; const png_size_t row_bytes, const png_size_t lmins)
|
|
align 4
|
|
proc png_setup_paeth_row, png_ptr:dword, bpp:dword, row_bytes:dword, lmins:dword
|
|
; bytep rp, dp, pp, cp, lp;
|
|
; png_size_t i;
|
|
; png_size_t sum = 0;
|
|
; int v;
|
|
|
|
; png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
|
|
|
|
; for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
|
|
; pp = png_ptr->prev_row + 1; i < bpp; i++)
|
|
; {
|
|
; v = *dp++ = (byte)(((int)*rp++ - (int)*pp++) & 0xff);
|
|
|
|
if PNG_USE_ABS eq 1
|
|
; sum += 128 - abs(v - 128);
|
|
else
|
|
; sum += (v < 128) ? v : 256 - v;
|
|
end if
|
|
; }
|
|
|
|
; for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
|
|
; i++)
|
|
; {
|
|
; int a, b, c, pa, pb, pc, p;
|
|
|
|
; b = *pp++;
|
|
; c = *cp++;
|
|
; a = *lp++;
|
|
|
|
; p = b - c;
|
|
; pc = a - c;
|
|
|
|
if PNG_USE_ABS eq 1
|
|
; pa = abs(p);
|
|
; pb = abs(pc);
|
|
; pc = abs(p + pc);
|
|
else
|
|
; pa = p < 0 ? -p : p;
|
|
; pb = pc < 0 ? -pc : pc;
|
|
; pc = (p + pc) < 0 ? -(p + pc) : p + pc;
|
|
end if
|
|
|
|
; p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
|
|
|
|
; v = *dp++ = (byte)(((int)*rp++ - p) & 0xff);
|
|
|
|
if PNG_USE_ABS eq 1
|
|
; sum += 128 - abs(v - 128);
|
|
else
|
|
; sum += (v < 128) ? v : 256 - v;
|
|
end if
|
|
|
|
; if (sum > lmins) /* We are already worse, don't continue. */
|
|
; break;
|
|
; }
|
|
|
|
; return (sum);
|
|
ret
|
|
endp
|
|
|
|
;void (png_structrp png_ptr, const uint_32 bpp, const png_size_t row_bytes)
|
|
align 4
|
|
proc png_setup_paeth_row_only, png_ptr:dword, bpp:dword, row_bytes:dword
|
|
; bytep rp, dp, pp, cp, lp;
|
|
; png_size_t i;
|
|
|
|
; png_ptr->try_row[0] = PNG_FILTER_VALUE_PAETH;
|
|
|
|
; for (i = 0, rp = png_ptr->row_buf + 1, dp = png_ptr->try_row + 1,
|
|
; pp = png_ptr->prev_row + 1; i < bpp; i++)
|
|
; {
|
|
; *dp++ = (byte)(((int)*rp++ - (int)*pp++) & 0xff);
|
|
; }
|
|
|
|
; for (lp = png_ptr->row_buf + 1, cp = png_ptr->prev_row + 1; i < row_bytes;
|
|
; i++)
|
|
; {
|
|
; int a, b, c, pa, pb, pc, p;
|
|
|
|
; b = *pp++;
|
|
; c = *cp++;
|
|
; a = *lp++;
|
|
|
|
; p = b - c;
|
|
; pc = a - c;
|
|
|
|
if PNG_USE_ABS eq 1
|
|
; pa = abs(p);
|
|
; pb = abs(pc);
|
|
; pc = abs(p + pc);
|
|
else
|
|
; pa = p < 0 ? -p : p;
|
|
; pb = pc < 0 ? -pc : pc;
|
|
; pc = (p + pc) < 0 ? -(p + pc) : p + pc;
|
|
end if
|
|
|
|
; p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
|
|
|
|
; *dp++ = (byte)(((int)*rp++ - p) & 0xff);
|
|
; }
|
|
ret
|
|
endp
|
|
|
|
;void (png_structrp png_ptr, png_row_infop row_info)
|
|
align 4
|
|
proc png_write_find_filter, png_ptr:dword, row_info:dword
|
|
locals
|
|
filter_to_do dd ? ;unsigned int ;= png_ptr->do_filter
|
|
row_buf dd ? ;bytep
|
|
best_row dd ? ;bytep
|
|
bpp dd ? ;uint_32
|
|
mins dd ? ;png_size_t
|
|
row_bytes dd ? ;png_size_t ;= row_info->rowbytes
|
|
endl
|
|
pushad
|
|
mov edi,[png_ptr]
|
|
if PNG_WRITE_FILTER_SUPPORTED eq 0
|
|
mov eax,[edi+png_struct.row_number]
|
|
png_debug1 2, ' (3)= %d', eax
|
|
mov eax,[edi+png_struct.rowbytes]
|
|
inc eax
|
|
stdcall png_write_filtered_row, edi, [edi+png_struct.row_buf], eax
|
|
mov eax,[edi+png_struct.row_number]
|
|
png_debug1 2, ' (4)= %d', eax
|
|
else
|
|
mov esi,[row_info]
|
|
movzx eax,byte[edi+png_struct.do_filter]
|
|
mov [filter_to_do],eax
|
|
mov eax,[esi+png_row_info.rowbytes]
|
|
mov [row_bytes],eax
|
|
|
|
png_debug 1, 'in png_write_find_filter'
|
|
|
|
|
|
; Find out how many bytes offset each pixel is
|
|
movzx eax,byte[edi+png_struct.pixel_depth]
|
|
add eax,7
|
|
shr eax,3
|
|
mov [bpp],eax
|
|
|
|
mov eax,[edi+png_struct.row_buf]
|
|
mov [row_buf],eax
|
|
mov dword[mins], PNG_SIZE_MAX - 256 ;so we can detect potential overflow of the
|
|
;running sum
|
|
|
|
; The prediction method we use is to find which method provides the
|
|
; smallest value when summing the absolute values of the distances
|
|
; from zero, using anything >= 128 as negative numbers. This is known
|
|
; as the "minimum sum of absolute differences" heuristic. Other
|
|
; heuristics are the "weighted minimum sum of absolute differences"
|
|
; (experimental and can in theory improve compression), and the "zlib
|
|
; predictive" method (not implemented yet), which does test compressions
|
|
; of lines using different filter methods, and then chooses the
|
|
; (series of) filter(s) that give minimum compressed data size (VERY
|
|
; computationally expensive).
|
|
|
|
; GRR 980525: consider also
|
|
|
|
; (1) minimum sum of absolute differences from running average (i.e.,
|
|
; keep running sum of non-absolute differences & count of bytes)
|
|
; [track dispersion, too? restart average if dispersion too large?]
|
|
|
|
; (1b) minimum sum of absolute differences from sliding average, probably
|
|
; with window size <= deflate window (usually 32K)
|
|
|
|
; (2) minimum sum of squared differences from zero or running average
|
|
; (i.e., ~ root-mean-square approach)
|
|
|
|
|
|
|
|
; We don't need to test the 'no filter' case if this is the only filter
|
|
; that has been chosen, as it doesn't actually do anything to the data.
|
|
|
|
mov eax,[edi+png_struct.row_buf]
|
|
mov [best_row],eax
|
|
|
|
cmp dword[row_bytes],PNG_SIZE_MAX/128
|
|
jl @f ;if (..>=..)
|
|
; Overflow can occur in the calculation, just select the lowest set
|
|
; filter.
|
|
|
|
xor eax,eax
|
|
sub eax,[filter_to_do]
|
|
and [filter_to_do],eax
|
|
jmp .end0
|
|
@@:
|
|
mov eax,[filter_to_do]
|
|
and eax,PNG_FILTER_NONE
|
|
jz .end0
|
|
cmp dword[filter_to_do],PNG_FILTER_NONE
|
|
je .end0 ;else if (..!=0 && ..!=..)
|
|
; Overflow not possible and multiple filters in the list, including the
|
|
; 'none' filter.
|
|
|
|
push esi
|
|
xor eax,eax
|
|
xor ebx,ebx
|
|
mov ecx,[row_bytes]
|
|
mov esi,[row_buf]
|
|
.cycle0:
|
|
lodsb
|
|
png_setup_abs ebx
|
|
loop .cycle0
|
|
pop esi
|
|
mov [mins],ebx
|
|
.end0:
|
|
|
|
; Sub filter
|
|
mov eax,[filter_to_do]
|
|
cmp eax,PNG_FILTER_SUB
|
|
jne @f ;if (..==..)
|
|
; It's the only filter so no testing is needed
|
|
stdcall png_setup_sub_row_only, edi, [bpp], [row_bytes]
|
|
mov eax,[edi+png_struct.try_row]
|
|
mov [best_row],eax
|
|
jmp .end1
|
|
@@:
|
|
and eax,PNG_FILTER_SUB
|
|
jz .end1 ;else if (..!=0)
|
|
stdcall png_setup_sub_row, edi, [bpp], [row_bytes], [mins]
|
|
cmp eax,[mins]
|
|
jge .end1 ;if (..<..)
|
|
mov [mins],eax
|
|
mov eax,[edi+png_struct.try_row]
|
|
mov [best_row],eax
|
|
test eax,eax
|
|
jz .end1 ;if (..!=0)
|
|
mov eax,[edi+png_struct.tst_row]
|
|
mov [edi+png_struct.try_row],eax
|
|
mov eax,[best_row]
|
|
mov [edi+png_struct.tst_row],eax
|
|
.end1:
|
|
|
|
; Up filter
|
|
mov eax,[filter_to_do]
|
|
cmp eax,PNG_FILTER_UP
|
|
jne @f ;if (..==..)
|
|
; It's the only filter so no testing is needed
|
|
stdcall png_setup_up_row_only, edi, [row_bytes]
|
|
mov eax,[edi+png_struct.try_row]
|
|
mov [best_row],eax
|
|
jmp .end2
|
|
@@:
|
|
and eax,PNG_FILTER_UP
|
|
jz .end2 ;else if (..!=0)
|
|
stdcall png_setup_up_row, edi, [row_bytes], [mins]
|
|
cmp eax,[mins]
|
|
jge .end2 ;if (..<..)
|
|
mov [mins],eax
|
|
mov eax,[edi+png_struct.try_row]
|
|
mov [best_row],eax
|
|
test eax,eax
|
|
jz .end2 ;if (..!=0)
|
|
mov eax,[edi+png_struct.tst_row]
|
|
mov [edi+png_struct.try_row],eax
|
|
mov eax,[best_row]
|
|
mov [edi+png_struct.tst_row],eax
|
|
.end2:
|
|
|
|
; Avg filter
|
|
mov eax,[filter_to_do]
|
|
cmp eax,PNG_FILTER_AVG
|
|
jne @f ;if (..==..)
|
|
; It's the only filter so no testing is needed
|
|
stdcall png_setup_avg_row_only, edi, [bpp], [row_bytes]
|
|
mov eax,[edi+png_struct.try_row]
|
|
mov [best_row],eax
|
|
jmp .end3
|
|
@@:
|
|
and eax,PNG_FILTER_AVG
|
|
jz .end3 ;else if (..!=0)
|
|
stdcall png_setup_avg_row, edi, [bpp], [row_bytes], [mins]
|
|
cmp eax,[mins]
|
|
jge .end3 ;if (..<..)
|
|
mov [mins],eax
|
|
mov eax,[edi+png_struct.try_row]
|
|
mov [best_row],eax
|
|
test eax,eax
|
|
jz .end3 ;if (..!=0)
|
|
mov eax,[edi+png_struct.tst_row]
|
|
mov [edi+png_struct.try_row],eax
|
|
mov eax,[best_row]
|
|
mov [edi+png_struct.tst_row],eax
|
|
.end3:
|
|
if 0 ;;; tmp
|
|
; Paeth filter
|
|
mov eax,[filter_to_do]
|
|
cmp eax,PNG_FILTER_PAETH
|
|
jne @f ;if (..==..)
|
|
; It's the only filter so no testing is needed
|
|
stdcall png_setup_paeth_row_only, edi, [bpp], [row_bytes]
|
|
mov eax,[edi+png_struct.try_row]
|
|
mov [best_row],eax
|
|
jmp .end4
|
|
@@:
|
|
and eax,PNG_FILTER_PAETH
|
|
jz .end4 ;else if (..!=0)
|
|
stdcall png_setup_paeth_row, edi, [bpp], [row_bytes], [mins]
|
|
cmp eax,[mins]
|
|
jge .end4 ;if (..<..)
|
|
mov [mins],eax
|
|
mov eax,[edi+png_struct.try_row]
|
|
mov [best_row],eax
|
|
test eax,eax
|
|
jz .end4 ;if (..!=0)
|
|
mov eax,[edi+png_struct.tst_row]
|
|
mov [edi+png_struct.try_row],eax
|
|
mov eax,[best_row]
|
|
mov [edi+png_struct.tst_row],eax
|
|
.end4:
|
|
end if
|
|
; Do the actual writing of the filtered row data from the chosen filter.
|
|
mov eax,[esi+png_row_info.rowbytes]
|
|
inc eax
|
|
stdcall png_write_filtered_row, edi, [best_row], eax
|
|
end if ;WRITE_FILTER
|
|
popad
|
|
ret
|
|
endp
|
|
|
|
|
|
; Do the actual writing of a previously filtered row.
|
|
;void (png_structrp png_ptr, bytep filtered_row,
|
|
; png_size_t full_row_length/*includes filter byte*/)
|
|
align 4
|
|
proc png_write_filtered_row uses eax ebx edi, png_ptr:dword, filtered_row:dword, full_row_length:dword
|
|
png_debug 1, 'in png_write_filtered_row'
|
|
|
|
mov eax,[filtered_row]
|
|
movzx eax,byte[eax]
|
|
png_debug1 2, 'filter = %d', eax
|
|
|
|
mov edi,[png_ptr]
|
|
stdcall png_compress_IDAT, [filtered_row], [full_row_length], Z_NO_FLUSH
|
|
|
|
if PNG_WRITE_FILTER_SUPPORTED eq 1
|
|
; Swap the current and previous rows
|
|
mov eax,[edi+png_struct.prev_row]
|
|
test eax,eax
|
|
jz @f ;if (..!=0)
|
|
;eax = tptr
|
|
mov ebx,[edi+png_struct.row_buf]
|
|
mov [edi+png_struct.prev_row],ebx
|
|
mov [edi+png_struct.row_buf],eax
|
|
@@:
|
|
end if ;WRITE_FILTER
|
|
|
|
; Finish row - updates counters and flushes zlib if last row
|
|
stdcall png_write_finish_row, edi
|
|
|
|
if PNG_WRITE_FLUSH_SUPPORTED eq 1
|
|
inc dword[edi+png_struct.flush_rows]
|
|
|
|
mov eax,[edi+png_struct.flush_dist]
|
|
cmp eax,0
|
|
jle @f
|
|
cmp [edi+png_struct.flush_rows],eax
|
|
jl @f ;if (..>0 && ..>=..)
|
|
stdcall png_write_flush, edi
|
|
@@:
|
|
end if ;WRITE_FLUSH
|
|
ret
|
|
endp
|