;;============================================================================;; ;;//// libimg.asm //// (c) mike.dld, 2007-2008 ;; ;; //// (c) diamond, 2009, ;; ;; //// (c) dunkaist, 2011-2020 ;; ;;============================================================================;; ;; ;; ;; This file is part of Common development libraries (Libs-Dev). ;; ;; ;; ;; Libs-Dev is free software: you can redistribute it and/or modify it under ;; ;; the terms of the GNU Lesser General Public License as published by the ;; ;; Free Software Foundation, either version 2.1 of the License, or (at your ;; ;; option) any later version. ;; ;; ;; ;; Libs-Dev is distributed in the hope that it will be useful, but WITHOUT ;; ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ;; ;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public ;; ;; License for more details. ;; ;; ;; ;; You should have received a copy of the GNU Lesser General Public License ;; ;; along with Libs-Dev. If not, see . ;; ;; ;; ;;============================================================================;; format MS COFF public @EXPORT as 'EXPORTS' include 'struct.inc' include 'proc32.inc' include 'macros.inc' include 'config.inc' include 'debug-fdo.inc' __DEBUG__ = 0 __DEBUG_LEVEL__ = 1 include 'libio.inc' purge section,mov,add,sub include 'libimg.inc' section '.flat' code readable align 16 include 'bmp/bmp.asm' include 'gif/gif.asm' include 'jpeg/jpeg.asm' include 'png/png.asm' include 'tga/tga.asm' include 'z80/z80.asm' include 'ico_cur/ico_cur.asm' include 'pcx/pcx.asm' include 'xcf/xcf.asm' include 'tiff/tiff.asm' include 'pnm/pnm.asm' include 'wbmp/wbmp.asm' include 'xbm/xbm.asm' include 'scale.asm' include 'convert.asm' COMPOSITE_MODE equ MMX ; MMX | pretty fast and compatible ; SSE | a bit faster, but may be unsupported by some CPUs include 'blend.asm' ;;============================================================================;; proc lib_init ;///////////////////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Library entry point (called after library load) ;; ;;----------------------------------------------------------------------------;; ;> eax = pointer to memory allocation routine ;; ;> ebx = pointer to memory freeing routine ;; ;> ecx = pointer to memory reallocation routine ;; ;> edx = pointer to library loading routine ;; ;;----------------------------------------------------------------------------;; ;< eax = 1 (fail) / 0 (ok) (library initialization result) ;; ;;============================================================================;; mov [mem.alloc], eax mov [mem.free], ebx mov [mem.realloc], ecx mov [dll.load], edx or edx, edx jz @f invoke dll.load, @IMPORT @@: call img.initialize.jpeg xor eax, eax cpuid cmp ecx, 'ntel' jnz @f mov dword [img._.do_rgb.handlers + (Image.bpp15-1)*4], img._.do_rgb.bpp15.intel mov dword [img._.do_rgb.handlers + (Image.bpp16-1)*4], img._.do_rgb.bpp16.intel @@: .ok: xor eax, eax ret endp ;;============================================================================;; proc img.is_img _data, _length ;//////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< --- TBD --- ;; ;;============================================================================;; push ebx mov ebx, img.formats_table @@: stdcall [ebx + FormatsTableEntry.Is], [_data], [_length] or eax, eax jnz @f add ebx, sizeof.FormatsTableEntry cmp dword[ebx], 0 jnz @b xor eax, eax @@: pop ebx ret endp ;;============================================================================;; proc img.info _data, _length ;////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< --- TBD --- ;; ;;============================================================================;; xor eax, eax ret endp ;;============================================================================;; proc img.from_file _filename ;////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? load file from disk and decode it ;; ;;----------------------------------------------------------------------------;; ;> [_filename] = file name as passed to libio ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 / pointer to image ;; ;;============================================================================;; locals fd dd ? img_data_len dd ? img_data dd ? ; raw bytes img dd ? ; Image pointer endl push ebx mov [img], 0 invoke file.open, [_filename], O_READ mov [fd], eax test eax, eax jz .exit invoke file.size, [_filename] test eax, eax jnz .exit_close mov [img_data_len], ebx invoke mem.alloc, ebx test eax, eax jz .exit_close mov [img_data], eax invoke file.read, [fd], eax, [img_data_len] cmp eax, -1 jz .exit_free_close cmp eax, [img_data_len] jnz .exit_free_close stdcall img.decode, [img_data], [img_data_len], 0 test eax, eax jz .exit_free_close mov [img], eax .exit_free_close: invoke mem.free, [img_data] .exit_close: invoke file.close, [fd] mov eax, [img] .exit: pop ebx ret endp ;;============================================================================;; proc img.to_file _img, _filename ;////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< eax = false / true ;; ;;============================================================================;; xor eax, eax ret endp ;;============================================================================;; proc img.from_rgb _rgb_data ;/////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 / pointer to image ;; ;;============================================================================;; xor eax, eax ret endp ;;============================================================================;; proc img.to_rgb2 _img, _out ;/////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? decodes image data into RGB triplets and stores them where [_out] points to;; ;;----------------------------------------------------------------------------;; ;> [_img] = pointer to source image ;; ;> [_out] = where to store RGB triplets ;; ;;----------------------------------------------------------------------------;; ;< none ;; ;;============================================================================;; push esi edi mov esi, [_img] stdcall img._.validate, esi or eax, eax jnz .ret mov edi, [_out] call img._.do_rgb .ret: pop edi esi ret endp ;;============================================================================;; proc img.to_rgb _img ;////////////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? decodes image data into RGB triplets and returns pointer to memory area of ;; ;? following structure: ;; ;? width dd ? ;; ;? height dd ? ;; ;? rgb triplets ;; ;;----------------------------------------------------------------------------;; ;> [_img] = pointer to source image ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 / pointer to rgb_data (array of [rgb] triplets) ;; ;;============================================================================;; push esi edi mov esi, [_img] stdcall img._.validate, esi or eax, eax jnz .error mov esi, [_img] mov ecx, [esi + Image.Width] imul ecx, [esi + Image.Height] lea eax, [ecx * 3 + 4 * 3] invoke mem.alloc, eax or eax, eax jz .error mov edi, eax push eax mov eax, [esi + Image.Width] stosd mov eax, [esi + Image.Height] stosd call img._.do_rgb pop eax pop edi esi ret .error: xor eax, eax pop edi esi ret endp ;;============================================================================;; proc img._.do_rgb ;///////////////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? decodes [esi + Image.Data] data into RGB triplets and stores them at [edi] ;; ;;----------------------------------------------------------------------------;; ;> esi = pointer to source image ;; ;> edi = pointer to memory to store RGB triplets ;; ;;----------------------------------------------------------------------------;; ;< none ;; ;;============================================================================;; mov ecx, [esi + Image.Width] imul ecx, [esi + Image.Height] mov eax, [esi + Image.Type] jmp dword [.handlers + (eax-1)*4] align 16 .bpp8i: ; 8 BPP WITH PALETTE -> 24 BPP push ebx mov ebx, [esi + Image.Palette] mov esi, [esi + Image.Data] sub ecx, 1 jz .bpp8i.last @@: movzx eax, byte [esi] add esi, 1 mov eax, [ebx + eax*4] mov [edi], eax add edi, 3 sub ecx, 1 jnz @b .bpp8i.last: movzx eax, byte [esi] mov eax, [ebx + eax*4] mov [edi], ax shr eax, 16 mov [edi+2], al pop ebx ret align 16 .bpp8g: ; 8 BPP GRAYSCALE -> 24 BPP mov esi, [esi + Image.Data] @@: lodsb mov ah, al stosb stosw dec ecx jnz @b ret ; ;align 16 ;.bpp8a: ; considered application layer, may be changed in the future ;; 8a BPP -> 24 BPP ; mov esi, [esi + Image.Data] ;@@: ; lodsw ; mov ah, al ; stosb ; stosw ; dec ecx ; jnz @b ; ret ; 15 BPP -> 24 BPP .bpp15.intel: push ebx ebp sub ecx, 4 jb .bpp15.tail align 16 .bpp15.intel.loop: repeat 2 mov ebx, [esi] mov al, [esi] mov ah, [esi+1] add esi, 4 and al, 0x1F and ah, 0x1F shl 2 mov ebp, ebx mov dl, al mov dh, ah shr al, 2 shr ah, 4 shl dl, 3 shl dh, 1 and ebp, 0x1F shl 5 add al, dl add ah, dh shr ebp, 2 mov [edi], al mov [edi+2], ah mov eax, ebx mov ebx, ebp shr eax, 16 shr ebx, 5 add ebx, ebp mov ebp, eax mov [edi+1], bl and eax, (0x1F) or (0x1F shl 10) and ebp, 0x1F shl 5 lea edx, [eax+eax] shr al, 2 mov ebx, ebp shr ah, 4 shl dl, 2 shr ebx, 2 shr ebp, 7 add al, dl add ah, dh mov [edi+3], al add ebx, ebp mov [edi+5], ah mov [edi+4], bl add edi, 6 end repeat sub ecx, 4 jnb .bpp15.intel.loop .bpp15.tail: add ecx, 4 jz .bpp15.done @@: movzx eax, word [esi] mov ebx, eax add esi, 2 and eax, (0x1F) or (0x1F shl 10) and ebx, 0x1F shl 5 lea edx, [eax+eax] shr al, 2 mov ebp, ebx shr ebx, 2 shr ah, 4 shl dl, 2 shr ebp, 7 add eax, edx add ebx, ebp mov [edi], al mov [edi+1], bl mov [edi+2], ah add edi, 3 sub ecx, 1 jnz @b .bpp15.done: pop ebp ebx ret .bpp15.amd: push ebx ebp sub ecx, 4 jb .bpp15.tail align 16 .bpp15.amd.loop: repeat 4 if (% mod 2) = 1 mov eax, dword [esi] mov ebx, dword [esi] else movzx eax, word [esi] mov ebx, eax end if add esi, 2 and eax, (0x1F) or (0x1F shl 10) and ebx, 0x1F shl 5 lea edx, [eax+eax] shr al, 2 mov ebp, ebx shr ebx, 2 shr ah, 4 shl dl, 2 shr ebp, 7 add eax, edx add ebx, ebp mov [edi], al mov [edi+1], bl mov [edi+2], ah add edi, 3 end repeat sub ecx, 4 jnb .bpp15.amd.loop jmp .bpp15.tail ; 16 BPP -> 24 BPP .bpp16.intel: push ebx ebp sub ecx, 4 jb .bpp16.tail align 16 .bpp16.intel.loop: repeat 2 mov ebx, [esi] mov al, [esi] mov ah, [esi+1] add esi, 4 and al, 0x1F and ah, 0x1F shl 3 mov ebp, ebx mov dl, al mov dh, ah shr al, 2 shr ah, 5 shl dl, 3 and ebp, 0x3F shl 5 add al, dl add ah, dh shr ebp, 3 mov [edi], al mov [edi+2], ah mov eax, ebx mov ebx, ebp shr eax, 16 shr ebx, 6 add ebx, ebp mov ebp, eax mov [edi+1], bl and eax, (0x1F) or (0x1F shl 11) and ebp, 0x3F shl 5 mov edx, eax shr al, 2 mov ebx, ebp shr ah, 5 shl dl, 3 shr ebx, 3 shr ebp, 9 add al, dl add ah, dh mov [edi+3], al add ebx, ebp mov [edi+5], ah mov [edi+4], bl add edi, 6 end repeat sub ecx, 4 jnb .bpp16.intel.loop .bpp16.tail: add ecx, 4 jz .bpp16.done @@: movzx eax, word [esi] mov ebx, eax add esi, 2 and eax, (0x1F) or (0x1F shl 11) and ebx, 0x3F shl 5 mov edx, eax shr al, 2 mov ebp, ebx shr ebx, 3 shr ah, 5 shl dl, 3 shr ebp, 9 add eax, edx add ebx, ebp mov [edi], al mov [edi+1], bl mov [edi+2], ah add edi, 3 sub ecx, 1 jnz @b .bpp16.done: pop ebp ebx ret .bpp16.amd: push ebx ebp sub ecx, 4 jb .bpp16.tail align 16 .bpp16.amd.loop: repeat 4 if (% mod 2) = 1 mov eax, dword [esi] mov ebx, dword [esi] else movzx eax, word [esi] mov ebx, eax end if add esi, 2 and eax, (0x1F) or (0x1F shl 11) and ebx, 0x3F shl 5 mov edx, eax shr al, 2 mov ebp, ebx shr ebx, 3 shr ah, 5 shl dl, 3 shr ebp, 9 add eax, edx add ebx, ebp mov [edi], al mov [edi+1], bl mov [edi+2], ah add edi, 3 end repeat sub ecx, 4 jnb .bpp16.amd.loop jmp .bpp16.tail align 16 .bpp24: ; 24 BPP -> 24 BPP lea ecx, [ecx*3 + 3] mov esi, [esi + Image.Data] shr ecx, 2 rep movsd ret align 16 .bpp32: ; 32 BPP -> 24 BPP mov esi, [esi + Image.Data] @@: mov eax, [esi] mov [edi], ax shr eax, 16 mov [edi+2], al add esi, 4 add edi, 3 sub ecx, 1 jnz @b @@: ret align 16 .bpp1: push ebx ebp mov ebp, [esi + Image.Width] mov edx, [esi + Image.Height] shl edx, 16 mov ebx, [esi + Image.Palette] mov esi, [esi + Image.Data] .bpp1.pre: mov dx, bp mov ecx, 7 .bpp1.begin: xor eax, eax bt [esi], ecx adc eax, 0 mov eax, [ebx + eax*4] mov [edi], ax shr eax, 16 mov [edi + 2], al add edi, 3 dec dx jz .bpp1.end_line dec ecx jns .bpp1.begin mov ecx, 7 inc esi jmp .bpp1.begin .bpp1.end_line: sub edx, 0x10000 jz .bpp1.quit inc esi jmp .bpp1.pre .bpp1.quit: pop ebp ebx ret align 16 .bpp2i: push ebx ebp mov ebp, [esi + Image.Width] mov edx, [esi + Image.Height] shl edx, 16 mov ebx, [esi + Image.Palette] mov esi, [esi + Image.Data] .bpp2i.pre: mov dx, bp mov ecx, 3 .bpp2i.begin: mov eax, 3 shl ecx, 1 shl eax, cl and al, [esi] shr eax, cl shr ecx, 1 mov eax, [ebx + eax*4] mov [edi], ax shr eax, 16 mov [edi + 2], al add edi, 3 dec dx jz .bpp2i.end_line dec ecx jns .bpp2i.begin mov ecx, 3 inc esi jmp .bpp2i.begin .bpp2i.end_line: sub edx, 0x10000 jz .bpp2i.quit inc esi jmp .bpp2i.pre .bpp2i.quit: pop ebp ebx ret align 16 .bpp4i: push ebx ebp mov ebp, [esi + Image.Width] mov edx, [esi + Image.Height] shl edx, 16 mov ebx, [esi + Image.Palette] mov esi, [esi + Image.Data] .bpp4i.pre: mov dx, bp mov ecx, 1 .bpp4i.begin: mov eax, 15 shl ecx, 2 shl eax, cl and al, [esi] shr eax, cl shr ecx, 2 mov eax, [ebx + eax*4] mov [edi], ax shr eax, 16 mov [edi + 2], al add edi, 3 dec dx jz .bpp4i.end_line dec ecx jns .bpp4i.begin mov ecx, 1 inc esi jmp .bpp4i.begin .bpp4i.end_line: sub edx, 0x10000 jz .bpp4i.quit inc esi jmp .bpp4i.pre .bpp4i.quit: pop ebp ebx ret endp ;;============================================================================;; proc img.decode _data, _length, _options ;////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? decodes loaded into memory graphic file ;; ;;----------------------------------------------------------------------------;; ;> [_data] = pointer to file in memory ;; ;> [_length] = size in bytes of memory area pointed to by [_data] ;; ;> [_options] = 0 / pointer to the structure of additional options ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 / pointer to image ;; ;;============================================================================;; push ebx mov ebx, [_length] or ebx, ebx jz .fail mov ebx, img.formats_table @@: stdcall [ebx + FormatsTableEntry.Is], [_data], [_length] or eax, eax jnz @f add ebx, sizeof.FormatsTableEntry cmp dword[ebx], eax ;0 jnz @b .fail: xor eax, eax pop ebx ret @@: mov eax, [ebx + FormatsTableEntry.Decode] pop ebx leave jmp eax endp ;;============================================================================;; proc img.encode uses ebx, _img, _common, _specific ;//////////////////////////;; ;;----------------------------------------------------------------------------;; ;? encode image to some format ;; ;;----------------------------------------------------------------------------;; ;> [_img] = pointer to input image ;; ;> [_common] = some most important/common options ;; ; 0x00 : byte : format id (defined in libimg.inc) ;; ; 0x01 : byte : fast encoding (0) / best compression ratio (255) ;; ; 0 : store uncompressed data (if supported both by the ;; ; format and libimg) ;; ; 1 - 255 : use compression, if supported ;; ; this option may be ignored if any format specific ;; ; options are defined, i.e. 0 here will be ignored if ;; ; particular compression algorithm is specified ;; ; 0x02 : byte : flags (bitfield) ;; ; 0x01 : return an error if format specific conditions ;; ; cannot be met ;; ; 0x02 : preserve current bit depth. means 8bpp/16bpp/ ;; ; 24bpp and so on ;; ; 0x04 : delete alpha channel, if any ;; ; 0x08 : flush alpha channel with 0xff, if any; add it if ;; ; none ;; ; 0x03 : byte : reserved, must be 0 ;; ;> [_specific] = 0 / pointer to the structure of format specific options ;; ; see .inc for description ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 / pointer to encoded data ;; ;< ecx = error code / the size of encoded data ;; ; 1 : out of memory ;; ; 2 : format is not supported ;; ; 3 : specific conditions cannot be satisfied ;; ; 4 : bit depth cannot be preserved ;; ;;============================================================================;; mov ebx, [_img] movzx eax, byte[_common] dec eax imul eax, sizeof.FormatsTableEntry add eax, FormatsTableEntry.Capabilities add eax, img.formats_table mov eax, [eax] test eax, 1 ; is encoding to this format supported at all? jnz @f mov ecx, LIBIMG_ERROR_FORMAT jmp .error @@: mov ecx, [ebx + Image.Type] mov edx, 1 shl edx, cl test eax, edx jnz .bit_depth_ok test byte[_common+2], LIBIMG_ENCODE_STRICT_BIT_DEPTH jz @f mov ecx, LIBIMG_ERROR_BIT_DEPTH jmp .error @@: mov edx, 1 SHL Image.bpp24 test eax, edx jnz @f mov ecx, LIBIMG_ERROR_BIT_DEPTH jmp .error @@: stdcall img.create, [ebx + Image.Width], [ebx + Image.Height], Image.bpp24 test eax, eax jnz @f mov ecx, LIBIMG_ERROR_OUT_OF_MEMORY jmp .error @@: push eax stdcall img.to_rgb2, ebx, [eax + Image.Data] pop ebx .bit_depth_ok: movzx eax, byte[_common] dec eax imul eax, sizeof.FormatsTableEntry add eax, FormatsTableEntry.Encode add eax, img.formats_table mov eax, [eax] stdcall eax, [_img], [_common], [_specific] push eax ecx cmp ebx, [_img] je @f stdcall img.destroy, ebx @@: pop ecx eax jmp .quit .error: xor eax, eax .quit: ret endp ;;============================================================================;; proc img.create _width, _height, _type ;//////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? creates an Image structure and initializes some its fields ;; ;;----------------------------------------------------------------------------;; ;> [_width] = width of an image in pixels ;; ;> [_height] = height of an image in pixels ;; ;> [_type] = one of the Image.bppN constants from libimg.inc ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 / pointer to image ;; ;;============================================================================;; push ecx stdcall img._.new or eax, eax jz .error mov ecx, [_type] mov [eax + Image.Type], ecx push eax stdcall img.resize_data, eax, [_width], [_height] or eax, eax jz .error.2 pop eax jmp .ret .error.2: ; pop eax stdcall img._.delete; eax xor eax, eax .error: .ret: pop ecx ret endp ;;============================================================================;; proc img.destroy.layer _img ;/////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Frees memory occupied by an image and all the memory regions its fields ;; ;? point to. For image sequences deletes only one frame and fixes Previous/ ;; ;? Next pointers. ;; ;;----------------------------------------------------------------------------;; ;> [_img] = pointer to image ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 (fail) / 1 (success) ;; ;;============================================================================;; mov eax, [_img] mov edx, [eax + Image.Previous] test edx, edx jz @f push [eax + Image.Next] pop [edx + Image.Next] @@: mov edx, [eax + Image.Next] test edx, edx jz @f push [eax + Image.Previous] pop [edx + Image.Previous] @@: stdcall img._.delete, eax ret endp ;;============================================================================;; proc img.destroy _img ;///////////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Frees memory occupied by an image and all the memory regions its fields ;; ;? point to. Follows Previous/Next pointers and deletes all the images in ;; ;? sequence. ;; ;;----------------------------------------------------------------------------;; ;> [_img] = pointer to image ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 (fail) / 1 (success) ;; ;;============================================================================;; push 1 mov eax, [_img] mov eax, [eax + Image.Previous] .destroy_prev_loop: test eax, eax jz .destroy_prev_done pushd [eax + Image.Previous] stdcall img._.delete, eax test eax, eax jnz @f mov byte [esp+4], 0 @@: pop eax jmp .destroy_prev_loop .destroy_prev_done: mov eax, [_img] .destroy_next_loop: pushd [eax + Image.Next] stdcall img._.delete, eax test eax, eax jnz @f mov byte [esp+4], 0 @@: pop eax test eax, eax jnz .destroy_next_loop pop eax ret endp ;;============================================================================;; proc img.count _img ;/////////////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Get number of images in the list (e.g. in animated GIF file) ;; ;;----------------------------------------------------------------------------;; ;> _img = pointer to image ;; ;;----------------------------------------------------------------------------;; ;< eax = -1 (fail) / >0 (ok) ;; ;;============================================================================;; push ecx edx mov edx, [_img] stdcall img._.validate, edx or eax, eax jnz .error @@: mov eax, [edx + Image.Previous] or eax, eax jz @f mov edx, eax jmp @b @@: xor ecx, ecx @@: inc ecx mov eax, [edx + Image.Next] or eax, eax jz .exit mov edx, eax jmp @b .exit: mov eax, ecx pop edx ecx ret .error: or eax, -1 pop edx ecx ret endp ;;//// image processing //////////////////////////////////////////////////////;; ;;============================================================================;; proc img.lock_bits _img, _start_line, _end_line ;/////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 / pointer to bits ;; ;;============================================================================;; xor eax, eax ret endp ;;============================================================================;; proc img.unlock_bits _img, _lock ;////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< eax = false / true ;; ;;============================================================================;; xor eax, eax ret endp ;;============================================================================;; proc img.flip.layer _img, _flip_kind ;////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Flip image layer ;; ;;----------------------------------------------------------------------------;; ;> _img = pointer to image ;; ;> _flip_kind = one of FLIP_* constants ;; ;;----------------------------------------------------------------------------;; ;< eax = false / true ;; ;;============================================================================;; locals scanline_len dd ? endl push ebx esi edi mov ebx, [_img] stdcall img._.validate, ebx or eax, eax jnz .error mov ecx, [ebx + Image.Height] mov eax, [ebx + Image.Width] call img._.get_scanline_len mov [scanline_len], eax test [_flip_kind], FLIP_VERTICAL jz .dont_flip_vert imul eax, ecx sub eax, [scanline_len] shr ecx, 1 mov esi, [ebx + Image.Data] lea edi, [esi + eax] .next_line_vert: push ecx mov ecx, [scanline_len] push ecx shr ecx, 2 @@: dec ecx js @f mov eax, [esi] xchg eax, [edi] mov [esi], eax add esi, 4 add edi, 4 jmp @b @@: pop ecx and ecx, 3 jz .cont_line_vert @@: mov al, [esi] xchg al, [edi] mov [esi], al add esi, 1 add edi, 1 dec ecx jnz @b .cont_line_vert: pop ecx mov eax, [scanline_len] shl eax, 1 sub edi, eax dec ecx jnz .next_line_vert .dont_flip_vert: test [_flip_kind], FLIP_HORIZONTAL jz .exit mov ecx, [ebx + Image.Height] mov eax, [ebx + Image.Type] mov esi, [ebx + Image.Data] mov edi, [scanline_len] add edi, esi jmp dword [.handlers_horz + (eax-1)*4] .bpp32_horz: sub edi, 4 .next_line_horz: push ecx esi edi mov ecx, [scanline_len] shr ecx, 3 @@: mov eax, [esi] xchg eax, [edi] mov [esi], eax add esi, 4 add edi, -4 sub ecx, 1 jnz @b pop edi esi ecx add esi, [scanline_len] add edi, [scanline_len] dec ecx jnz .next_line_horz jmp .exit .bpp1x_horz: sub edi, 2 .next_line_horz1x: push ecx esi edi mov ecx, [ebx + Image.Width] @@: mov ax, [esi] mov dx, [edi] mov [edi], ax mov [esi], dx add esi, 2 sub edi, 2 sub ecx, 2 ja @b pop edi esi ecx add esi, [scanline_len] add edi, [scanline_len] dec ecx jnz .next_line_horz1x jmp .exit .bpp8ig_horz: dec edi .next_line_horz8ig: push ecx esi edi mov ecx, [scanline_len] shr ecx, 1 @@: mov al, [esi] mov dl, [edi] mov [edi], al mov [esi], dl add esi, 1 sub edi, 1 sub ecx, 1 jnz @b pop edi esi ecx add esi, [scanline_len] add edi, [scanline_len] dec ecx jnz .next_line_horz8ig jmp .exit .bpp24_horz: sub edi, 3 .next_line_horz24: push ecx esi edi mov ecx, [ebx + Image.Width] @@: mov al, [esi] mov dl, [edi] mov [edi], al mov [esi], dl mov al, [esi+1] mov dl, [edi+1] mov [edi+1], al mov [esi+1], dl mov al, [esi+2] mov dl, [edi+2] mov [edi+2], al mov [esi+2], dl add esi, 3 sub edi, 3 sub ecx, 2 ja @b pop edi esi ecx add esi, [scanline_len] add edi, [scanline_len] dec ecx jnz .next_line_horz24 jmp .exit .bpp1_horz: mov edi, [scanline_len] mov edx, [ebx + Image.Width] and edx, 7 neg edx add edx, 8 and edx, 7 .bpp1_horz.begin: push ebx edx esi mov eax, 7 add edi, esi sub edi, 1 mov ebx, [ebx + Image.Width] shr ebx, 1 .bpp1_horz.bit: bt [edi], edx jc @f btr [esi], eax jmp .bpp1_horz.right @@: bts [esi], eax .bpp1_horz.right: jnc @f bts [edi], edx jmp .bpp1_horz.bit_done @@: btr [edi], edx .bpp1_horz.bit_done: inc edx and edx, 7 jnz @f dec edi @@: dec eax jns @f mov eax, 7 inc esi @@: dec ebx jnz .bpp1_horz.bit pop esi edx ebx add esi, [scanline_len] mov edi, [scanline_len] dec ecx jnz .bpp1_horz.begin jmp .exit .bpp2i_horz: mov edi, [scanline_len] mov edx, [ebx + Image.Width] and edx, 3 neg edx add edx, 4 and edx, 3 .bpp2i_horz.begin: push ebx edx esi mov eax, 3 add edi, esi sub edi, 1 mov ebx, [ebx + Image.Width] shr ebx, 1 .bpp2i_horz.pixel: push ebx ecx mov ebx, 3 mov ecx, edx shl ebx, cl shl ebx, cl and bl, [edi] shr ebx, cl shr ebx, cl mov bh, 3 mov ecx, eax shl ebx, cl shl ebx, cl not bh and bh, [esi] or bl, bh mov bh, [esi] mov [esi], bl shr ebx, 8 shr ebx, cl shr ebx, cl and ebx, 3 mov bh, 3 mov ecx, edx shl ebx, cl shl ebx, cl not bh and bh, [edi] or bl, bh mov [edi], bl pop ecx ebx .bpp2i_horz.pixel_done: inc edx and edx, 3 jnz @f dec edi @@: dec eax jns @f mov eax, 3 inc esi @@: dec ebx jnz .bpp2i_horz.pixel pop esi edx ebx add esi, [scanline_len] mov edi, [scanline_len] dec ecx jnz .bpp2i_horz.begin jmp .exit .bpp4i_horz: mov edi, [scanline_len] mov edx, [ebx + Image.Width] and edx, 1 neg edx add edx, 2 and edx, 1 .bpp4i_horz.begin: push ebx edx esi mov eax, 1 add edi, esi sub edi, 1 mov ebx, [ebx + Image.Width] shr ebx, 1 .bpp4i_horz.pixel: push ebx ecx mov ebx, 15 mov ecx, edx shl ecx, 2 shl ebx, cl and bl, [edi] shr ebx, cl mov bh, 15 mov ecx, eax shl ecx, 2 shl ebx, cl not bh and bh, [esi] or bl, bh mov bh, [esi] mov [esi], bl shr ebx, 8 shr ebx, cl and ebx, 15 mov bh, 15 mov ecx, edx shl ecx, 2 shl ebx, cl not bh and bh, [edi] or bl, bh mov [edi], bl pop ecx ebx .bpp4i_horz.pixel_done: inc edx and edx, 1 jnz @f dec edi @@: dec eax jns @f mov eax, 1 inc esi @@: dec ebx jnz .bpp4i_horz.pixel pop esi edx ebx add esi, [scanline_len] mov edi, [scanline_len] dec ecx jnz .bpp4i_horz.begin jmp .exit .exit: xor eax, eax inc eax pop edi esi ebx ret .error: xor eax, eax pop edi esi ebx ret endp ;;============================================================================;; proc img.flip _img, _flip_kind ;//////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Flip all layers of image ;; ;;----------------------------------------------------------------------------;; ;> _img = pointer to image ;; ;> _flip_kind = one of FLIP_* constants ;; ;;----------------------------------------------------------------------------;; ;< eax = false / true ;; ;;============================================================================;; push 1 mov ebx, [_img] @@: mov eax, [ebx + Image.Previous] test eax, eax jz .loop mov ebx, eax jmp @b .loop: stdcall img.flip.layer, ebx, [_flip_kind] test eax, eax jnz @f mov byte [esp], 0 @@: mov ebx, [ebx + Image.Next] test ebx, ebx jnz .loop pop eax ret endp ;;============================================================================;; proc img.rotate.layer _img, _rotate_kind ;////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Rotate image layer ;; ;;----------------------------------------------------------------------------;; ;> _img = pointer to image ;; ;> _rotate_kind = one of ROTATE_* constants ;; ;;----------------------------------------------------------------------------;; ;< eax = false / true ;; ;;============================================================================;; locals scanline_len_old dd ? scanline_len_new dd ? scanline_pixels_new dd ? line_buffer dd ? pixels_ptr dd ? endl mov [line_buffer], 0 push ebx esi edi mov ebx, [_img] stdcall img._.validate, ebx or eax, eax jnz .error cmp [_rotate_kind], ROTATE_90_CCW je .rotate_ccw_low cmp [_rotate_kind], ROTATE_90_CW je .rotate_cw_low cmp [_rotate_kind], ROTATE_180 je .flip jmp .exit .rotate_ccw_low: mov eax, [ebx + Image.Height] mov [scanline_pixels_new], eax call img._.get_scanline_len mov [scanline_len_new], eax invoke mem.alloc, eax or eax, eax jz .error mov [line_buffer], eax mov eax, [ebx + Image.Width] mov ecx, eax call img._.get_scanline_len mov [scanline_len_old], eax mov eax, [scanline_len_new] imul eax, ecx add eax, [ebx + Image.Data] mov [pixels_ptr], eax cmp [ebx + Image.Type], Image.bpp1 jz .rotate_ccw1 cmp [ebx + Image.Type], Image.bpp2i jz .rotate_ccw2i cmp [ebx + Image.Type], Image.bpp4i jz .rotate_ccw4i cmp [ebx + Image.Type], Image.bpp8i jz .rotate_ccw8ig cmp [ebx + Image.Type], Image.bpp8g jz .rotate_ccw8ig cmp [ebx + Image.Type], Image.bpp24 jz .rotate_ccw24 cmp [ebx + Image.Type], Image.bpp32 jz .rotate_ccw32 .next_column_ccw_low1x: dec ecx js .exchange_dims push ecx mov edx, [scanline_len_old] add [scanline_len_old], -2 mov ecx, [scanline_pixels_new] mov esi, [ebx + Image.Data] mov edi, [line_buffer] @@: mov ax, [esi] mov [edi], ax add esi, edx add edi, 2 sub ecx, 1 jnz @b mov eax, [scanline_pixels_new] mov edi, [ebx + Image.Data] lea esi, [edi + 2] mov edx, [scanline_len_old] @@: mov ecx, edx shr ecx, 2 rep movsd mov ecx, edx and ecx, 3 rep movsb add esi, 1 sub eax, 1 jnz @b mov eax, [scanline_len_new] sub [pixels_ptr], eax mov ecx, [scanline_pixels_new] mov esi, [line_buffer] mov edi, [pixels_ptr] mov edx, ecx shr ecx, 2 rep movsd mov ecx, edx and ecx, 3 rep movsb pop ecx jmp .next_column_ccw_low1x .rotate_ccw32: .next_column_ccw_low: dec ecx js .exchange_dims push ecx mov edx, [scanline_len_old] add [scanline_len_old], -4 mov ecx, [scanline_pixels_new] mov esi, [ebx + Image.Data] mov edi, [line_buffer] @@: mov eax, [esi] stosd add esi, edx dec ecx jnz @b mov eax, [scanline_pixels_new] mov edi, [ebx + Image.Data] lea esi, [edi + 4] mov edx, [scanline_len_old] shr edx, 2 @@: mov ecx, edx rep movsd add esi, 4 dec eax jnz @b mov eax, [scanline_len_new] sub [pixels_ptr], eax mov ecx, [scanline_pixels_new] mov esi, [line_buffer] mov edi, [pixels_ptr] rep movsd pop ecx jmp .next_column_ccw_low .rotate_ccw8ig: .next_column_ccw_low8ig: dec ecx js .exchange_dims push ecx mov edx, [scanline_len_old] add [scanline_len_old], -1 mov ecx, [scanline_pixels_new] mov esi, [ebx + Image.Data] mov edi, [line_buffer] @@: mov al, [esi] mov [edi], al add esi, edx add edi, 1 sub ecx, 1 jnz @b mov eax, [scanline_pixels_new] mov edi, [ebx + Image.Data] lea esi, [edi + 1] mov edx, [scanline_len_old] @@: mov ecx, edx shr ecx, 2 rep movsd mov ecx, edx and ecx, 3 rep movsb add esi, 1 sub eax, 1 jnz @b mov eax, [scanline_len_new] sub [pixels_ptr], eax mov ecx, [scanline_pixels_new] mov esi, [line_buffer] mov edi, [pixels_ptr] mov edx, ecx shr ecx, 2 rep movsd mov ecx, edx and ecx, 3 rep movsb pop ecx jmp .next_column_ccw_low8ig .rotate_ccw24: .next_column_ccw_low24: dec ecx js .exchange_dims push ecx mov edx, [scanline_len_old] add [scanline_len_old], -3 mov ecx, [scanline_pixels_new] mov esi, [ebx + Image.Data] mov edi, [line_buffer] @@: mov al, [esi] mov [edi], al mov al, [esi+1] mov [edi+1], al mov al, [esi+2] mov [edi+2], al add esi, edx add edi, 3 sub ecx, 1 jnz @b mov eax, [scanline_pixels_new] mov edi, [ebx + Image.Data] lea esi, [edi + 3] mov edx, [scanline_len_old] @@: mov ecx, edx shr ecx, 2 rep movsd mov ecx, edx and ecx, 3 rep movsb add esi, 3 sub eax, 1 jnz @b mov eax, [scanline_len_new] sub [pixels_ptr], eax mov ecx, eax mov esi, [line_buffer] mov edi, [pixels_ptr] shr ecx, 2 rep movsd mov ecx, eax and ecx, 3 rep movsb pop ecx jmp .next_column_ccw_low24 .rotate_ccw1: push ecx edx mov eax, [ebx + Image.Height] add eax, 7 shr eax, 3 imul eax, [ebx + Image.Width] push eax ; save new data size invoke mem.alloc, eax or eax, eax jz .error push eax ; save pointer to new data mov ecx, [ebx + Image.Width] and ecx, 7 neg ecx add ecx, 8 and ecx, 7 mov edi, eax mov esi, [ebx + Image.Data] mov eax, 7 mov edx, [scanline_len_old] dec edx add esi, edx .rotate_ccw1.begin: bt [esi], ecx jc .rotate_ccw1.one .rotate_ccw1.zero: btr [edi], eax jmp @f .rotate_ccw1.one: bts [edi], eax @@: add esi, [scanline_len_old] dec [scanline_pixels_new] jz .rotate_ccw1.end_of_line sub eax, 1 adc edi, 0 and eax, 7 jmp .rotate_ccw1.begin .rotate_ccw1.end_of_line: inc edi mov eax, [ebx + Image.Height] mov [scanline_pixels_new], eax mov eax, 7 inc ecx and ecx, 7 jz @f mov esi, [ebx + Image.Data] add esi, edx jmp .rotate_ccw1.begin @@: dec edx js .rotate_ccw1.quit mov esi, [ebx + Image.Data] add esi, edx jmp .rotate_ccw1.begin .rotate_ccw1.quit: pop esi ; get pointer to new data mov edi, [ebx + Image.Data] pop ecx ; get new data size rep movsb invoke mem.free, esi pop edx ecx jmp .exchange_dims .rotate_ccw2i: push ecx edx mov eax, [ebx + Image.Height] add eax, 3 shr eax, 2 imul eax, [ebx + Image.Width] push eax ; save new data size invoke mem.alloc, eax or eax, eax jz .error push eax ; save pointer to new data mov ecx, [ebx + Image.Width] and ecx, 3 neg ecx add ecx, 4 and ecx, 3 mov edi, eax mov esi, [ebx + Image.Data] mov eax, 3 mov edx, [scanline_len_old] dec edx add esi, edx .rotate_ccw2i.begin: push ebx ecx mov ebx, 3 shl ebx, cl shl ebx, cl and bl, [esi] shr ebx, cl shr ebx, cl mov bh, 3 mov ecx, eax shl ebx, cl shl ebx, cl not bh and bh, [edi] or bl, bh mov [edi], bl pop ecx ebx add esi, [scanline_len_old] dec [scanline_pixels_new] jz .rotate_ccw2i.end_of_line sub eax, 1 adc edi, 0 and eax, 3 jmp .rotate_ccw2i.begin .rotate_ccw2i.end_of_line: inc edi mov eax, 3 mov esi, [ebx + Image.Height] mov [scanline_pixels_new], esi inc ecx and ecx, 3 jz @f mov esi, [ebx + Image.Data] add esi, edx jmp .rotate_ccw2i.begin @@: dec edx js .rotate_ccw2i.quit mov esi, [ebx + Image.Data] add esi, edx jmp .rotate_ccw2i.begin .rotate_ccw2i.quit: pop esi ; get pointer to new data mov edi, [ebx + Image.Data] pop ecx ; get new data size rep movsb invoke mem.free, esi pop edx ecx jmp .exchange_dims .rotate_ccw4i: push ecx edx mov eax, [ebx + Image.Height] add eax, 1 shr eax, 1 imul eax, [ebx + Image.Width] push eax ; save new data size invoke mem.alloc, eax or eax, eax jz .error push eax ; save pointer to new data mov ecx, [ebx + Image.Width] and ecx, 1 neg ecx add ecx, 2 and ecx, 1 mov edi, eax mov esi, [ebx + Image.Data] mov eax, 1 mov edx, [scanline_len_old] dec edx add esi, edx .rotate_ccw4i.begin: push ebx ecx mov ebx, 15 shl ecx, 2 shl ebx, cl and bl, [esi] shr ebx, cl mov bh, 15 mov ecx, eax shl ecx, 2 shl ebx, cl not bh and bh, [edi] or bl, bh mov [edi], bl pop ecx ebx add esi, [scanline_len_old] dec [scanline_pixels_new] jz .rotate_ccw4i.end_of_line sub eax, 1 adc edi, 0 and eax, 1 jmp .rotate_ccw4i.begin .rotate_ccw4i.end_of_line: inc edi mov eax, 1 mov esi, [ebx + Image.Height] mov [scanline_pixels_new], esi inc ecx and ecx, 1 jz @f mov esi, [ebx + Image.Data] add esi, edx jmp .rotate_ccw4i.begin @@: dec edx js .rotate_ccw4i.quit mov esi, [ebx + Image.Data] add esi, edx jmp .rotate_ccw4i.begin .rotate_ccw4i.quit: pop esi ; get pointer to new data mov edi, [ebx + Image.Data] pop ecx ; get new data size rep movsb invoke mem.free, esi pop edx ecx jmp .exchange_dims .rotate_cw_low: mov eax, [ebx + Image.Height] mov [scanline_pixels_new], eax call img._.get_scanline_len mov [scanline_len_new], eax invoke mem.alloc, eax or eax, eax jz .error mov [line_buffer], eax mov eax, [ebx + Image.Width] mov ecx, eax call img._.get_scanline_len mov [scanline_len_old], eax mov eax, [scanline_len_new] imul eax, ecx add eax, [ebx + Image.Data] mov [pixels_ptr], eax cmp [ebx + Image.Type], Image.bpp1 jz .rotate_cw1 cmp [ebx + Image.Type], Image.bpp2i jz .rotate_cw2i cmp [ebx + Image.Type], Image.bpp4i jz .rotate_cw4i cmp [ebx + Image.Type], Image.bpp8i jz .rotate_cw8ig cmp [ebx + Image.Type], Image.bpp8g jz .rotate_cw8ig cmp [ebx + Image.Type], Image.bpp24 jz .rotate_cw24 cmp [ebx + Image.Type], Image.bpp32 jz .rotate_cw32 .next_column_cw_low1x: dec ecx js .exchange_dims push ecx mov edx, [scanline_len_old] add [scanline_len_old], -2 mov ecx, [scanline_pixels_new] mov esi, [pixels_ptr] add esi, -2 mov edi, [line_buffer] @@: mov ax, [esi] mov [edi], ax sub esi, edx add edi, 2 sub ecx, 1 jnz @b mov eax, [scanline_pixels_new] dec eax mov edi, [ebx + Image.Data] add edi, [scanline_len_old] lea esi, [edi + 2] mov edx, [scanline_len_old] @@: mov ecx, edx shr ecx, 2 rep movsd mov ecx, edx and ecx, 3 rep movsb add esi, 3 sub eax, 1 jnz @b mov eax, [scanline_len_new] sub [pixels_ptr], eax mov ecx, eax mov esi, [line_buffer] mov edi, [pixels_ptr] shr ecx, 2 rep movsd mov ecx, eax and ecx, 3 rep movsb pop ecx jmp .next_column_cw_low1x .rotate_cw32: .next_column_cw_low: dec ecx js .exchange_dims push ecx mov edx, [scanline_len_old] add [scanline_len_old], -4 mov ecx, [scanline_pixels_new] mov esi, [pixels_ptr] add esi, -4 mov edi, [line_buffer] @@: mov eax, [esi] stosd sub esi, edx dec ecx jnz @b mov eax, [scanline_pixels_new] dec eax mov edi, [ebx + Image.Data] add edi, [scanline_len_old] lea esi, [edi + 4] mov edx, [scanline_len_old] shr edx, 2 @@: mov ecx, edx rep movsd add esi, 4 dec eax jnz @b mov eax, [scanline_len_new] sub [pixels_ptr], eax mov ecx, [scanline_pixels_new] mov esi, [line_buffer] mov edi, [pixels_ptr] rep movsd pop ecx jmp .next_column_cw_low .rotate_cw8ig: .next_column_cw_low8ig: dec ecx js .exchange_dims push ecx mov edx, [scanline_len_old] add [scanline_len_old], -1 mov ecx, [scanline_pixels_new] mov esi, [pixels_ptr] add esi, -1 mov edi, [line_buffer] @@: mov al, [esi] mov [edi], al sub esi, edx add edi, 1 sub ecx, 1 jnz @b mov eax, [scanline_pixels_new] dec eax mov edi, [ebx + Image.Data] add edi, [scanline_len_old] lea esi, [edi + 1] mov edx, [scanline_len_old] @@: mov ecx, edx shr ecx, 2 rep movsd mov ecx, edx and ecx, 3 rep movsb add esi, 1 sub eax, 1 jnz @b mov eax, [scanline_len_new] sub [pixels_ptr], eax mov ecx, eax mov esi, [line_buffer] mov edi, [pixels_ptr] shr ecx, 2 rep movsd mov ecx, eax and ecx, 3 rep movsb pop ecx jmp .next_column_cw_low8ig .rotate_cw24: .next_column_cw_low24: dec ecx js .exchange_dims push ecx mov edx, [scanline_len_old] add [scanline_len_old], -3 mov ecx, [scanline_pixels_new] mov esi, [pixels_ptr] add esi, -3 mov edi, [line_buffer] @@: mov al, [esi] mov [edi], al mov al, [esi+1] mov [edi+1], al mov al, [esi+2] mov [edi+2], al sub esi, edx add edi, 3 sub ecx, 1 jnz @b mov eax, [scanline_pixels_new] dec eax mov edi, [ebx + Image.Data] add edi, [scanline_len_old] lea esi, [edi + 3] mov edx, [scanline_len_old] @@: mov ecx, edx shr ecx, 2 rep movsd mov ecx, edx and ecx, 3 rep movsb add esi, 3 sub eax, 1 jnz @b mov eax, [scanline_len_new] sub [pixels_ptr], eax mov ecx, eax mov esi, [line_buffer] mov edi, [pixels_ptr] shr ecx, 2 rep movsd mov ecx, eax and ecx, 3 rep movsb pop ecx jmp .next_column_cw_low24 .rotate_cw1: push ecx edx mov eax, [ebx + Image.Height] add eax, 7 shr eax, 3 imul [ebx + Image.Width] push eax ; save new data size invoke mem.alloc, eax or eax, eax jz .error push eax ; save pointer to new data mov edi, eax mov esi, [ebx + Image.Data] mov eax, [ebx + Image.Height] dec eax imul eax, [scanline_len_old] add esi, eax mov eax, 7 mov ecx, 7 mov edx, 0 .rotate_cw1.begin: bt [esi], ecx jc .rotate_cw1.one .rotate_cw1.zero: btr [edi], eax jmp @f .rotate_cw1.one: bts [edi], eax @@: sub esi, [scanline_len_old] dec [scanline_pixels_new] jz .rotate_cw1.end_of_line sub eax, 1 adc edi, 0 and eax, 7 jmp .rotate_cw1.begin .rotate_cw1.end_of_line: inc edi mov eax, [ebx + Image.Height] mov [scanline_pixels_new], eax mov eax, 7 dec ecx jns @f mov ecx, 7 inc edx cmp edx, [scanline_len_old] je .rotate_cw1.quit @@: mov esi, [ebx + Image.Height] dec esi imul esi, [scanline_len_old] add esi, [ebx + Image.Data] add esi, edx jmp .rotate_cw1.begin .rotate_cw1.quit: pop eax ; get pointer to new data mov esi, eax mov edi, [ebx + Image.Data] pop ecx ; get new data size rep movsb invoke mem.free, eax pop edx ecx jmp .exchange_dims .rotate_cw2i: push ecx edx mov eax, [ebx + Image.Height] add eax, 3 shr eax, 2 imul [ebx + Image.Width] push eax ; save new data size invoke mem.alloc, eax or eax, eax jz .error push eax ; save pointer to new data mov edi, eax mov esi, [ebx + Image.Data] mov eax, [ebx + Image.Height] dec eax imul eax, [scanline_len_old] add esi, eax mov eax, 3 mov ecx, 3 mov edx, 0 .rotate_cw2i.begin: push ebx ecx mov ebx, 3 shl ebx, cl shl ebx, cl and bl, [esi] shr ebx, cl shr ebx, cl mov bh, 3 mov ecx, eax shl ebx, cl shl ebx, cl not bh and bh, [edi] or bl, bh mov [edi], bl pop ecx ebx sub esi, [scanline_len_old] dec [scanline_pixels_new] jz .rotate_cw2i.end_of_line sub eax, 1 adc edi, 0 and eax, 3 jmp .rotate_cw2i.begin .rotate_cw2i.end_of_line: inc edi mov eax, [ebx + Image.Height] mov [scanline_pixels_new], eax mov eax, 3 dec ecx jns @f mov ecx, 3 inc edx cmp edx, [scanline_len_old] je .rotate_cw2i.quit @@: mov esi, [ebx + Image.Height] dec esi imul esi, [scanline_len_old] add esi, [ebx + Image.Data] add esi, edx jmp .rotate_cw2i.begin .rotate_cw2i.quit: pop eax ; get pointer to new data mov esi, eax mov edi, [ebx + Image.Data] pop ecx ; get new data size rep movsb invoke mem.free, eax pop edx ecx jmp .exchange_dims .rotate_cw4i: push ecx edx mov eax, [ebx + Image.Height] add eax, 1 shr eax, 1 imul [ebx + Image.Width] push eax ; save new data size invoke mem.alloc, eax or eax, eax jz .error push eax ; save pointer to new data mov edi, eax mov esi, [ebx + Image.Data] mov eax, [ebx + Image.Height] dec eax imul eax, [scanline_len_old] add esi, eax mov eax, 1 mov ecx, 1 mov edx, 0 .rotate_cw4i.begin: push ebx ecx mov ebx, 15 shl ecx, 2 shl ebx, cl and bl, [esi] shr ebx, cl mov bh, 15 mov ecx, eax shl ecx, 2 shl ebx, cl not bh and bh, [edi] or bl, bh mov [edi], bl pop ecx ebx sub esi, [scanline_len_old] dec [scanline_pixels_new] jz .rotate_cw4i.end_of_line sub eax, 1 adc edi, 0 and eax, 1 jmp .rotate_cw4i.begin .rotate_cw4i.end_of_line: inc edi mov eax, [ebx + Image.Height] mov [scanline_pixels_new], eax mov eax, 1 dec ecx jns @f mov ecx, 1 inc edx cmp edx, [scanline_len_old] je .rotate_cw4i.quit @@: mov esi, [ebx + Image.Height] dec esi imul esi, [scanline_len_old] add esi, [ebx + Image.Data] add esi, edx jmp .rotate_cw4i.begin .rotate_cw4i.quit: pop eax ; get pointer to new data mov esi, eax mov edi, [ebx + Image.Data] pop ecx ; get new data size rep movsb invoke mem.free, eax pop edx ecx jmp .exchange_dims .flip: stdcall img.flip.layer, [_img], FLIP_VERTICAL test eax, eax jz .error jmp .exit .exchange_dims: push [ebx + Image.Width] [ebx + Image.Height] pop [ebx + Image.Width] [ebx + Image.Height] .exit: invoke mem.free, [line_buffer] xor eax, eax inc eax pop edi esi ebx ret .error: invoke mem.free, [line_buffer] xor eax, eax pop edi esi ebx ret endp ;;============================================================================;; proc img.rotate _img, _rotate_kind ;//////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Rotate all layers of image ;; ;;----------------------------------------------------------------------------;; ;> _img = pointer to image ;; ;> _rotate_kind = one of ROTATE_* constants ;; ;;----------------------------------------------------------------------------;; ;< eax = false / true ;; ;;============================================================================;; push 1 mov ebx, [_img] @@: mov eax, [ebx + Image.Previous] test eax, eax jz .loop mov ebx, eax jmp @b .loop: stdcall img.rotate.layer, ebx, [_rotate_kind] test eax, eax jnz @f mov byte [esp], 0 @@: mov ebx, [ebx + Image.Next] test ebx, ebx jnz .loop pop eax ret endp ;;============================================================================;; proc img.draw _img, _x, _y, _width, _height, _xpos, _ypos ;///////////////////;; ;;----------------------------------------------------------------------------;; ;? Draw image in the window ;; ;;----------------------------------------------------------------------------;; ;> _img = pointer to image ;; ;>_x = x-coordinate in the window ;; ;>_y = y-coordinate in the window ;; ;>_width = maximum width to draw ;; ;>_height = maximum height to draw ;; ;>_xpos = offset in image by x-axis ;; ;>_ypos = offset in image by y-axis ;; ;;----------------------------------------------------------------------------;; ;< no return value ;; ;;============================================================================;; push ebx esi edi mov ebx, [_img] stdcall img._.validate, ebx test eax, eax jnz .done mov ecx, [ebx + Image.Width] sub ecx, [_xpos] jbe .done cmp ecx, [_width] jb @f mov ecx, [_width] @@: mov edx, [ebx + Image.Height] sub edx, [_ypos] jbe .done cmp edx, [_height] jb @f mov edx, [_height] @@: mov eax, [ebx + Image.Width] sub eax, ecx call img._.get_scanline_len shl ecx, 16 add ecx, edx push eax mov eax, [ebx + Image.Width] imul eax, [_ypos] add eax, [_xpos] call img._.get_scanline_len add eax, [ebx + Image.Data] mov edx, [_x - 2] mov dx, word [_y] mov esi, [ebx + Image.Type] mov esi, [type2bpp + (esi-1)*4] mov edi, [ebx + Image.Palette] xchg eax, ebx pop eax push ebp push 65 pop ebp xchg eax, ebp int 40h pop ebp .done: pop edi esi ebx ret endp align 4 img.formats_table: .bmp dd LIBIMG_FORMAT_BMP, img.is.bmp, img.decode.bmp, img.encode.bmp, 1 + (1 SHL Image.bpp24) + (1 SHL Image.bpp32) .ico dd LIBIMG_FORMAT_ICO, img.is.ico, img.decode.ico_cur, img.encode.ico, 0 .cur dd LIBIMG_FORMAT_CUR, img.is.cur, img.decode.ico_cur, img.encode.cur, 0 .gif dd LIBIMG_FORMAT_GIF, img.is.gif, img.decode.gif, img.encode.gif, 0 .png dd LIBIMG_FORMAT_PNG, img.is.png, img.decode.png, img.encode.png, 1 + (1 SHL Image.bpp24) .jpg dd LIBIMG_FORMAT_JPEG, img.is.jpg, img.decode.jpg, img.encode.jpg, 0 .tga dd LIBIMG_FORMAT_TGA, img.is.tga, img.decode.tga, img.encode.tga, 0 .pcx dd LIBIMG_FORMAT_PCX, img.is.pcx, img.decode.pcx, img.encode.pcx, 0 .xcf dd LIBIMG_FORMAT_XCF, img.is.xcf, img.decode.xcf, img.encode.xcf, 0 .tiff dd LIBIMG_FORMAT_TIFF, img.is.tiff, img.decode.tiff, img.encode.tiff,0 .pnm dd LIBIMG_FORMAT_PNM, img.is.pnm, img.decode.pnm, img.encode.pnm, 1 + (1 SHL Image.bpp1) + (1 SHL Image.bpp8g) + (1 SHL Image.bpp24) .wbmp dd LIBIMG_FORMAT_WBMP, img.is.wbmp, img.decode.wbmp, img.encode.wbmp,0 .xbm dd LIBIMG_FORMAT_XBM, img.is.xbm, img.decode.xbm, img.encode.xbm, 0 .z80 dd LIBIMG_FORMAT_Z80, img.is.z80, img.decode.z80, img.encode.z80, 0 ;this must be the last entry as there are no signatures in z80 screens at all dd 0 ;;============================================================================;; ;;////////////////////////////////////////////////////////////////////////////;; ;;============================================================================;; ;! Below are private procs you should never call directly from your code ;; ;;============================================================================;; ;;////////////////////////////////////////////////////////////////////////////;; ;;============================================================================;; ;;============================================================================;; proc img._.validate, _img ;///////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< --- TBD --- ;; ;;============================================================================;; xor eax, eax ret endp ;;============================================================================;; proc img._.new ;//////////////////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 / pointer to image ;; ;;============================================================================;; invoke mem.alloc, sizeof.Image test eax, eax jz @f push ecx xor ecx, ecx mov [eax + Image.Data], ecx mov [eax + Image.Type], ecx mov [eax + Image.Flags], ecx mov [eax + Image.Extended], ecx mov [eax + Image.Previous], ecx mov [eax + Image.Next], ecx pop ecx @@: ret endp ;;============================================================================;; proc img._.delete _img ;//////////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;> --- TBD --- ;; ;;----------------------------------------------------------------------------;; ;< eax = false / true ;; ;;============================================================================;; push edx mov edx, [_img] cmp [edx + Image.Data], 0 je @f invoke mem.free, [edx + Image.Data] @@: cmp [edx + Image.Extended], 0 je @f invoke mem.free, [edx + Image.Extended] @@: invoke mem.free, edx pop edx ret endp ;;============================================================================;; proc img.resize_data _img, _width, _height ;//////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Resize data block of image. New size is calculated from _width and _height ;; ;? params and internal Image.Type value. All the internal fields are updated ;; ;? iff succeeded. This function does not scale images, use img.scale if you ;; ;? need to. ;; ;;----------------------------------------------------------------------------;; ;> _img = pointer to image ;; ;> _width = new width ;; ;> _height = new height ;; ;;----------------------------------------------------------------------------;; ;< eax = 0 (fail) / pointer to the new pixels data ;; ;;============================================================================;; push ebx esi mov ebx, [_img] mov eax, [_height] ; our memory is limited, [_width]*[_height] must not overflow ; image with width or height greater than 65535 is most likely bogus cmp word [_width+2], 0 jnz .error cmp word [_height+2], 0 jnz .error imul eax, [_width] test eax, eax jz .error cmp [ebx + Image.Type], Image.bpp1 jz .bpp1 cmp [ebx + Image.Type], Image.bpp2i jz .bpp2i cmp [ebx + Image.Type], Image.bpp4i jz .bpp4i cmp [ebx + Image.Type], Image.bpp8i jz .bpp8i cmp [ebx + Image.Type], Image.bpp8g jz .bpp8g cmp [ebx + Image.Type], Image.bpp8a jz .bpp8a cmp [ebx + Image.Type], Image.bpp24 jz .bpp24 .bpp32: shl eax, 2 jmp @f .bpp24: lea eax, [eax*3] jmp @f .bpp8i: add eax, 256*4 ; for palette .bpp8g: jmp @f .bpp8a: shl eax, 1 jmp @f .bpp4i: mov eax, [_width] add eax, 1 shr eax, 1 imul eax, [_height] mov ecx, eax mov eax, [_height] add eax, 1 shr eax, 1 imul eax, [_width] cmp eax, ecx jge .bpp4i.skip mov eax, ecx .bpp4i.skip: add eax, 16*4 ; for palette jmp @f .bpp2i: mov eax, [_width] add eax, 3 shr eax, 2 imul eax, [_height] mov ecx, eax mov eax, [_height] add eax, 3 shr eax, 2 imul eax, [_width] cmp eax, ecx jge .bpp2i.skip mov eax, ecx .bpp2i.skip: add eax, 4*4 ; for palette jmp @f .bpp1: mov eax, [_width] add eax, 7 shr eax, 3 imul eax, [_height] mov ecx, eax mov eax, [_height] add eax, 7 shr eax, 3 imul eax, [_width] cmp eax, ecx jge .bpp1.skip mov eax, ecx .bpp1.skip: add eax, 2*4 ; for palette @@: mov esi, eax invoke mem.realloc, [ebx + Image.Data], eax or eax, eax jz .error mov [ebx + Image.Data], eax push [_width] pop [ebx + Image.Width] push [_height] pop [ebx + Image.Height] cmp [ebx + Image.Type], Image.bpp8i jnz @f lea esi, [eax + esi - 256*4] mov [ebx + Image.Palette], esi jmp .ret @@: cmp [ebx + Image.Type], Image.bpp1 jnz @f lea esi, [eax + esi - 2*4] mov [ebx + Image.Palette], esi jmp .ret @@: cmp [ebx + Image.Type], Image.bpp2i jnz @f lea esi, [eax + esi - 4*4] mov [ebx + Image.Palette], esi jmp .ret @@: cmp [ebx + Image.Type], Image.bpp4i jnz .ret lea esi, [eax + esi - 16*4] mov [ebx + Image.Palette], esi jmp .ret .error: xor eax, eax .ret: pop esi ebx ret endp ;;============================================================================;; img._.get_scanline_len: ;/////////////////////////////////////////////////////;; ;;----------------------------------------------------------------------------;; ;? Get scanline length of image in bytes ;; ;;----------------------------------------------------------------------------;; ;> eax = width of image in pixels ;; ;> ebx = image ;; ;;----------------------------------------------------------------------------;; ;< eax = scanline length in bytes ;; ;;============================================================================;; cmp [ebx + Image.Type], Image.bpp1 jz .bpp1.1 cmp [ebx + Image.Type], Image.bpp2i jz .bpp2i.1 cmp [ebx + Image.Type], Image.bpp4i jz .bpp4i.1 cmp [ebx + Image.Type], Image.bpp8i jz .bpp8.1 cmp [ebx + Image.Type], Image.bpp8g jz .bpp8.1 cmp [ebx + Image.Type], Image.bpp8a jz .bpp8a.1 cmp [ebx + Image.Type], Image.bpp24 jz .bpp24.1 add eax, eax cmp [ebx + Image.Type], Image.bpp32 jnz .quit add eax, eax jmp .quit .bpp24.1: lea eax, [eax*3] jmp .quit .bpp1.1: add eax, 7 shr eax, 3 jmp .quit .bpp2i.1: add eax, 3 shr eax, 2 jmp .quit .bpp4i.1: add eax, 1 shr eax, 1 jmp .quit .bpp8a.1: shl eax, 1 .bpp8.1: .quit: ret ;;============================================================================;; ;;////////////////////////////////////////////////////////////////////////////;; ;;============================================================================;; ;! Below is private data you should never use directly from your code ;; ;;============================================================================;; ;;////////////////////////////////////////////////////////////////////////////;; ;;============================================================================;; section '.data' data readable writable align 16 ;include_debug_strings align 4 type2bpp dd 8, 24, 32, 15, 16, 1, 9, 2, 4 img._.do_rgb.handlers: dd img._.do_rgb.bpp8i dd img._.do_rgb.bpp24 dd img._.do_rgb.bpp32 dd img._.do_rgb.bpp15.amd ; can be overwritten in lib_init dd img._.do_rgb.bpp16.amd ; can be overwritten in lib_init dd img._.do_rgb.bpp1 dd img._.do_rgb.bpp8g dd img._.do_rgb.bpp2i dd img._.do_rgb.bpp4i img.flip.layer.handlers_horz: dd img.flip.layer.bpp8ig_horz dd img.flip.layer.bpp24_horz dd img.flip.layer.bpp32_horz dd img.flip.layer.bpp1x_horz dd img.flip.layer.bpp1x_horz dd img.flip.layer.bpp1_horz dd img.flip.layer.bpp8ig_horz dd img.flip.layer.bpp2i_horz dd img.flip.layer.bpp4i_horz ;;============================================================================;; ;;////////////////////////////////////////////////////////////////////////////;; ;;============================================================================;; ;! Exported functions section ;; ;;============================================================================;; ;;////////////////////////////////////////////////////////////////////////////;; ;;============================================================================;; align 4 @EXPORT: export \ lib_init , 'lib_init' , \ 0x00050007 , 'version' , \ img.is_img , 'img_is_img' , \ img.info , 'img_info' , \ img.from_file , 'img_from_file' , \ img.to_file , 'img_to_file' , \ img.from_rgb , 'img_from_rgb' , \ img.to_rgb , 'img_to_rgb' , \ img.to_rgb2 , 'img_to_rgb2' , \ img.decode , 'img_decode' , \ img.encode , 'img_encode' , \ ;supported formats: PNG 24 32, BMP 24 32, PNM 1 8g 24 img.create , 'img_create' , \ img.destroy , 'img_destroy' , \ img.destroy.layer , 'img_destroy_layer' , \ img.count , 'img_count' , \ img.lock_bits , 'img_lock_bits' , \ img.unlock_bits , 'img_unlock_bits' , \ img.flip , 'img_flip' , \ img.flip.layer , 'img_flip_layer' , \ img.rotate , 'img_rotate' , \ img.rotate.layer , 'img_rotate_layer' , \ img.draw , 'img_draw' , \ img.scale , 'img_scale' , \ img.get_scaled_size, 'img_get_scaled_size', \ img.convert , 'img_convert' , \ img.blend , 'img_blend' , \ img.resize_data , 'img_resize_data' , \ img.formats_table , 'img_formats_table' ; import from deflate unpacker ; is initialized only when PNG loading is requested align 16 @IMPORT: library \ archiver, 'archiver.obj', \ libio , 'libio.obj' import archiver, \ deflate_unpack2, 'deflate_unpack2',\ deflateInit2, 'deflateInit2',\ deflateReset, 'deflateReset',\ deflate, 'deflate',\ deflateEnd, 'deflateEnd',\ calc_crc32, 'calc_crc32' import libio , \ file.size , 'file_size' , \ file.open , 'file_open' , \ file.read , 'file_read' , \ file.close, 'file_close' align 4 ; mutex for unpacker loading deflate_loader_mutex dd 0 ; default palette for GIF - b&w gif_default_palette: db 0, 0, 0 db 0xFF, 0xFF, 0xFF ; uninitialized data - global constant tables mem.alloc dd ? mem.free dd ? mem.realloc dd ? dll.load dd ? ; data for YCbCr -> RGB translation color_table_1 rd 256 color_table_2 rd 256 color_table_3 rd 256 color_table_4 rd 256