Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 6732 → Rev 6733

/programs/develop/libraries/libs-dev/libimg/png/libpng/pngwutil.asm
0,0 → 1,3319
 
; 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 defined(PNG_DEBUG) && (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]
cmp edi,0
je .end_f ;if (png_ptr == NULL) 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
mov ebx,ebp
sub ebx,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
 
mov ebx,ebp
sub ebx,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]
cmp edi,0
je .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
cmp eax,0
je @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
cmp eax,0
je .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 word[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
cmp eax,0
je @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],ax
 
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 word[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
 
cmp eax,0
jne .end1 ;if (..==0)
PNG_COMPRESSION_BUFFER_SIZE edi
stdcall png_malloc, edi, eax
mov [next],eax
 
cmp eax,0
jne @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],ax
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.
 
movzx eax,word[edi+png_struct.zstream.avail_out]
sub [output_len],eax
mov word[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
cmp eax,0
jne .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
cmp eax,0
jne @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],ax
.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]
jle @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 word[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
cmp eax,0
jne @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],cx
 
; 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]
sub cx,[edi+png_struct.zstream.avail_out]
;edx = data
;ecx = size
 
if PNG_WRITE_OPTIMIZE_CMF_SUPPORTED eq 1
mov eax,[edi+png_struct.mode]
and eax,PNG_HAVE_IDAT
cmp eax,0
jne @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 word[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
 
cmp eax,0
jne @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
 
cmp eax,0
jne @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
 
cmp eax,0
jne @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
cmp eax,0
jne @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]
;png_debug1 2, ' row_number = %d', eax
;png_debug1 2, ' num_rows = %d', [edi+png_struct.num_rows]
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
cmp eax,0
je @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
cmp eax,0
jne .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 [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
mov ah,byte[ebx]
shr ah,1
sub al,ah
mov ah,byte[edx]
shr ah,1
sub al,ah
stosb
png_setup_abs [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 1
mov eax,[edi+png_struct.rowbytes]
inc eax
stdcall png_write_filtered_row, edi, [edi+png_struct.row_buf], eax
else
mov esi,[row_info]
mov eax,[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 PNG_SIZE_MAX/128, dword[row_bytes]
jg @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
cmp eax,0
je .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
cmp eax,0
je .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
cmp eax,0
je .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
cmp eax,0
je .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
cmp eax,0
je .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
cmp eax,0
je .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
cmp eax,0
je .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:
 
; 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
cmp eax,0
je .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
cmp eax,0
je .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:
 
; 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]
cmp eax,0
je @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