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 |