docs: def from_bytes(cls, b):
  		"""Create an APNG from raw bytes.
  		
  		:arg bytes b: The raw bytes of the APNG file.
  		:rtype: APNG
  		"""
  		hdr = None
  		head_chunks = []
  		end = ("IEND", make_chunk("IEND", b""))
  		
  		frame_chunks = []
  		frames = []
  		num_plays = 0
  		frame_has_head_chunks = False
  		
  		control = None
  		
  		for type_, data in parse_chunks(b):
  			if type_ == "IHDR":
  				hdr = data
  				frame_chunks.append((type_, data))
  			elif type_ == "acTL":
  				_num_frames, num_plays = struct.unpack("!II", data[8:-4])
  				continue
  			elif type_ == "fcTL":
  				if any(type_ == "IDAT" for type_, data in frame_chunks):
  					# IDAT inside chunk, go to next frame
  					frame_chunks.append(end)
  					frames.append((PNG.from_chunks(frame_chunks), control))
  					frame_has_head_chunks = False
  					control = FrameControl.from_bytes(data[12:-4])
  					# https://github.com/PyCQA/pylint/issues/2072
  					# pylint: disable=typecheck
  					hdr = make_chunk("IHDR", struct.pack("!II", control.width, control.height) + hdr[16:-4])
  					frame_chunks = [("IHDR", hdr)]
  				else:
  					control = FrameControl.from_bytes(data[12:-4])
  			elif type_ == "IDAT":
  				if not frame_has_head_chunks:
  					frame_chunks.extend(head_chunks)
  					frame_has_head_chunks = True
  				frame_chunks.append((type_, data))
  			elif type_ == "fdAT":
  				# convert to IDAT
  				if not frame_has_head_chunks:
  					frame_chunks.extend(head_chunks)
  					frame_has_head_chunks = True
  				frame_chunks.append(("IDAT", make_chunk("IDAT", data[12:-4])))
  			elif type_ == "IEND":
  				# end
  				frame_chunks.append(end)
  				frames.append((PNG.from_chunks(frame_chunks), control))
  				break
  			elif type_ in CHUNK_BEFORE_IDAT:
  				head_chunks.append((type_, data))
  			else:
  				frame_chunks.append((type_, data))
  				
  		o = cls()
  		o.frames = frames
  		o.num_plays = num_plays
  		return o