This commit is contained in:
multiSnow 2022-12-03 16:35:52 +08:00
parent 3fbdc87de4
commit b8ff014e8c
No known key found for this signature in database
GPG Key ID: 78D698D00CCDD6C4
3 changed files with 36 additions and 125 deletions

View File

@ -76,7 +76,6 @@ def _loadimg(im,blen=None):
depth=im.depth
# raw pixels
# bytearray
#pixels=im.make_blob()
pixels,croplen=_makeblob(im,width,height,blen=blen)
palette=b''
@ -91,113 +90,6 @@ def _loadimg(im,blen=None):
return geometry,bpp,depth,fmt,croplen,DataBuffer(pixels,palette)
def _loadimg2(im,blen=None):
# load all useful and only useful infomation from Image object.
# should called after Image.read() operation in the same process and thread.
# width, height, page width, page height, offset x, offset y
# unsigned long long, 'Q', for very very large image
width,height=im.size
page_width,page_height,*offset=im.page
geometry=width,height,(page_width or width),(page_height or height),*offset
# bytes per pixel
# unsigned char, 'B'
bpp=im.bpp
# bits per color
# unsigned char, 'B'
depth=im.depth
# color format name
# string
fmt=im.fmt
# raw pixels and palette, do override if needed.
# color in palette should always be RGBA
# bytes
pixels=palette=b''
match (fmt,bpp,depth):
case ('GRAY',1,1):
# override: (for low memory usage)
# pixels and bpp*depth are ok, but generate palette and change fmt to INDEX.
# palette only contains two color, black and white.
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt=fmt)
palette=bytes((0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff))
fmt='INDEX'
case ('GRAY',1,8):
# override: (for low memory usage)
# generate palette from pixels, remap pixels and change fmt to INDEX
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt=fmt)
palette=tuple(sorted(set(pixels)))
if len(palette)<0xff:
# NOTE: only 8 depth GRAY could do remap in this way
# (both pixel and index are 1 bytes char)
pairs=((p,n) for n,p in enumerate(palette) if n!=p)
pixels=pixels.translate(bytes.maketrans(*map(bytes,zip(*pairs))))
palette=bytes(c for pixel in ((l,l,l,255) for l in palette) for c in pixel)
fmt='INDEX'
case ('GRAYA',2,8):
# override:
# convert to 32 depth RGBA. no hack.
# TODO: pass pixels as-is to ui process
bpp=4
fmt='RGBA'
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt=fmt)
case ('INDEX',1,8):
# override: (for low memory usage while acceptable performance)
# generate palette from pixels and remap pixels.
# avoid call ctype function in loop (such as im.color_map). that costs to much.
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt='RGBA')
pixels=iter(pixels)
pixels=tuple(map(bytes,zip(*(pixels,)*4))) # 4 color in each pixel
palette=sorted(set(pixels))
d={p:n for n,p in enumerate(palette)}
pixels=bytes(d[p] for p in pixels)
palette=b''.join(palette)
case ('RGB',3,8):
# no override
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt=fmt)
case ('RGBA',4,8):
# no override
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt=fmt)
case ('CMYK',4,8):
# override:
# convert to 24 depth RGB. no hack.
bpp=3
fmt='RGB'
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt=fmt)
case ('CMYKA',5,8):
# override:
# convert to 24 depth RGB. no hack.
bpp=4
fmt='RGBA'
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt=fmt)
case _:
assert warn(__name__,'unknown pixel depth or color format:'
f'{bpp=} {depth=} {fmt=}')
bpp=4
depth=im.depth=8
fmt='RGBA'
pixels,croplen=_makeblob(im,width,height,blen=blen,fmt=fmt)
length=len(pixels)
size=width*height*bpp
# assert: pixels amount
assert length==size,f'pixel amount not match {length} {size}'
# assert: provide RGBA palette only for INDEX color format
if fmt=='INDEX':
assert palette,'no palette with INDEX pixfmt'
assert not len(palette)%4,'palette not in RGBA format'
else:
assert not palette,f'palette with {fmt} pixfmt'
return geometry,bpp,depth,fmt,croplen,DataBuffer(pixels,palette)
def py_padding_rgb24(pixels,width):
src_pitch=width*3
padding_len=(4-src_pitch)%4
@ -427,7 +319,15 @@ def generator_Anime(im,options=None,shrink=None):
assert trace(__name__,'generator_Anime: start')
n_frames=im.iterator_length()
first_size=None
for n,frame in enumerate(im.iter_frames(),1):
im.iterator_reset()
n=0
has_next=True
while has_next:
n+=1
frame=im.image_get()
if not im.iterator_next():
im.close()
has_next=False
if n==1 and shrink is not None:
# use value instead of reference
first_size=im.width,im.height
@ -446,10 +346,17 @@ def generator_Signle(im,options=None,shrink=None,blen=None):
# generator of (0, 1) and image raw data
assert (p:=Pref.start(name=f'{__name__}: generator'))
assert trace(__name__,'generator: start')
with im:
if im.iterator_length()>1:
# only load first frame
im.iterator_reset()
frame=im.image_get()
im.close()
assert p.record(name='get first frame')
# frame will be closed by load_single_frame
yield load_single_frame(im.image_get(),shrink=shrink,blen=blen)
yield load_single_frame(frame,shrink=shrink,blen=blen)
else:
# image will be closed by load_single_frame
yield load_single_frame(im,shrink=shrink,blen=blen)
assert p.record(name='end')
assert trace(__name__,'generator: end')
assert p.log()

View File

@ -25,7 +25,7 @@ library.MagickIdentifyImageType.restype = c_int
class Image(_Image):
supported_formats=tuple(formats())
_fmtmap={
# format of image type, ignore 'alpha'/'matte' type
# format of image type, ignore 'alpha'/'matte' in palette type
'bilevel':'GRAY',
'grayscale':'GRAY',
'grayscalealpha':'GRAYA',
@ -50,6 +50,11 @@ class Image(_Image):
'CMYKA':5, # need verify
}
def __new__(cls,*args,**kwds):
instance=super().__new__(cls)
instance._identified_type=None
return instance
def __init__(self,*args,file=None,filename=None,blob=None,options=None,**kwds):
blob_is_bytearray=isinstance(blob,bytearray)
if not options and not blob_is_bytearray or \
@ -68,9 +73,10 @@ class Image(_Image):
else:
self.read(file=file,filename=filename,blob=blob,**kwds)
identified_type=self.identified_type
if identified_type!=self.type:
if (identified_type:=self.identified_type)!=self.type:
self.type=identified_type
self.format=('RGBA' if self.alpha_channel else 'RGB') \
if self.fmt=='INDEX' else self.fmt
@classmethod
def ping(cls,file=None,filename=None,blob=None,**kwds):
@ -93,9 +99,12 @@ class Image(_Image):
@property
def identified_type(self):
# identified image type
if index:=library.MagickIdentifyImageType(self.wand):
return IMAGE_TYPES[index]
self.raise_exception()
if self._identified_type is None:
if index:=library.MagickIdentifyImageType(self.wand):
self._identified_type=IMAGE_TYPES[index]
else:
self.raise_exception()
return self._identified_type
@property
def fmt(self):
@ -122,13 +131,6 @@ class Image(_Image):
def clone(self):
return Image(image=self)
def iter_frames(self):
# iterator of each frame in animation
self.iterator_reset()
yield self.image_get()
while self.iterator_next():
yield self.image_get()
SUPPORTED_FORMATS=tuple(
(f,bytes.fromhex('ff'*len(s) if m is None else m),s) for f,m,s in (
('BMP', None, b'BM'),

View File

@ -503,7 +503,9 @@ class ENV(NonWidget):
assert note(__name__,f'{self}: anime stop',prettypath(*path))
self._animes.discard(path)
return
canvas=self.getcanvas(path)
if (canvas:=self.getcanvas(path)) is None:
self._animes.discard(path)
return
if not canvas.next_frame() and canvas.n_loop:
loop_count+=1
if loop_count>=canvas.n_loop: