Added support for multiple tags on a single model
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
#
|
||||
# The Python Imaging Library.
|
||||
# $Id$
|
||||
#
|
||||
# a Windows DIB display interface
|
||||
#
|
||||
# History:
|
||||
# 1996-05-20 fl Created
|
||||
# 1996-09-20 fl Fixed subregion exposure
|
||||
# 1997-09-21 fl Added draw primitive (for tzPrint)
|
||||
# 2003-05-21 fl Added experimental Window/ImageWindow classes
|
||||
# 2003-09-05 fl Added fromstring/tostring methods
|
||||
#
|
||||
# Copyright (c) Secret Labs AB 1997-2003.
|
||||
# Copyright (c) Fredrik Lundh 1996-2003.
|
||||
#
|
||||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
from . import Image
|
||||
|
||||
|
||||
class HDC:
|
||||
"""
|
||||
Wraps an HDC integer. The resulting object can be passed to the
|
||||
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
|
||||
methods.
|
||||
"""
|
||||
|
||||
def __init__(self, dc):
|
||||
self.dc = dc
|
||||
|
||||
def __int__(self):
|
||||
return self.dc
|
||||
|
||||
|
||||
class HWND:
|
||||
"""
|
||||
Wraps an HWND integer. The resulting object can be passed to the
|
||||
:py:meth:`~PIL.ImageWin.Dib.draw` and :py:meth:`~PIL.ImageWin.Dib.expose`
|
||||
methods, instead of a DC.
|
||||
"""
|
||||
|
||||
def __init__(self, wnd):
|
||||
self.wnd = wnd
|
||||
|
||||
def __int__(self):
|
||||
return self.wnd
|
||||
|
||||
|
||||
class Dib:
|
||||
"""
|
||||
A Windows bitmap with the given mode and size. The mode can be one of "1",
|
||||
"L", "P", or "RGB".
|
||||
|
||||
If the display requires a palette, this constructor creates a suitable
|
||||
palette and associates it with the image. For an "L" image, 128 graylevels
|
||||
are allocated. For an "RGB" image, a 6x6x6 colour cube is used, together
|
||||
with 20 graylevels.
|
||||
|
||||
To make sure that palettes work properly under Windows, you must call the
|
||||
``palette`` method upon certain events from Windows.
|
||||
|
||||
:param image: Either a PIL image, or a mode string. If a mode string is
|
||||
used, a size must also be given. The mode can be one of "1",
|
||||
"L", "P", or "RGB".
|
||||
:param size: If the first argument is a mode string, this
|
||||
defines the size of the image.
|
||||
"""
|
||||
|
||||
def __init__(self, image, size=None):
|
||||
if hasattr(image, "mode") and hasattr(image, "size"):
|
||||
mode = image.mode
|
||||
size = image.size
|
||||
else:
|
||||
mode = image
|
||||
image = None
|
||||
if mode not in ["1", "L", "P", "RGB"]:
|
||||
mode = Image.getmodebase(mode)
|
||||
self.image = Image.core.display(mode, size)
|
||||
self.mode = mode
|
||||
self.size = size
|
||||
if image:
|
||||
self.paste(image)
|
||||
|
||||
def expose(self, handle):
|
||||
"""
|
||||
Copy the bitmap contents to a device context.
|
||||
|
||||
:param handle: Device context (HDC), cast to a Python integer, or an
|
||||
HDC or HWND instance. In PythonWin, you can use
|
||||
``CDC.GetHandleAttrib()`` to get a suitable handle.
|
||||
"""
|
||||
if isinstance(handle, HWND):
|
||||
dc = self.image.getdc(handle)
|
||||
try:
|
||||
result = self.image.expose(dc)
|
||||
finally:
|
||||
self.image.releasedc(handle, dc)
|
||||
else:
|
||||
result = self.image.expose(handle)
|
||||
return result
|
||||
|
||||
def draw(self, handle, dst, src=None):
|
||||
"""
|
||||
Same as expose, but allows you to specify where to draw the image, and
|
||||
what part of it to draw.
|
||||
|
||||
The destination and source areas are given as 4-tuple rectangles. If
|
||||
the source is omitted, the entire image is copied. If the source and
|
||||
the destination have different sizes, the image is resized as
|
||||
necessary.
|
||||
"""
|
||||
if not src:
|
||||
src = (0, 0) + self.size
|
||||
if isinstance(handle, HWND):
|
||||
dc = self.image.getdc(handle)
|
||||
try:
|
||||
result = self.image.draw(dc, dst, src)
|
||||
finally:
|
||||
self.image.releasedc(handle, dc)
|
||||
else:
|
||||
result = self.image.draw(handle, dst, src)
|
||||
return result
|
||||
|
||||
def query_palette(self, handle):
|
||||
"""
|
||||
Installs the palette associated with the image in the given device
|
||||
context.
|
||||
|
||||
This method should be called upon **QUERYNEWPALETTE** and
|
||||
**PALETTECHANGED** events from Windows. If this method returns a
|
||||
non-zero value, one or more display palette entries were changed, and
|
||||
the image should be redrawn.
|
||||
|
||||
:param handle: Device context (HDC), cast to a Python integer, or an
|
||||
HDC or HWND instance.
|
||||
:return: A true value if one or more entries were changed (this
|
||||
indicates that the image should be redrawn).
|
||||
"""
|
||||
if isinstance(handle, HWND):
|
||||
handle = self.image.getdc(handle)
|
||||
try:
|
||||
result = self.image.query_palette(handle)
|
||||
finally:
|
||||
self.image.releasedc(handle, handle)
|
||||
else:
|
||||
result = self.image.query_palette(handle)
|
||||
return result
|
||||
|
||||
def paste(self, im, box=None):
|
||||
"""
|
||||
Paste a PIL image into the bitmap image.
|
||||
|
||||
:param im: A PIL image. The size must match the target region.
|
||||
If the mode does not match, the image is converted to the
|
||||
mode of the bitmap image.
|
||||
:param box: A 4-tuple defining the left, upper, right, and
|
||||
lower pixel coordinate. See :ref:`coordinate-system`. If
|
||||
None is given instead of a tuple, all of the image is
|
||||
assumed.
|
||||
"""
|
||||
im.load()
|
||||
if self.mode != im.mode:
|
||||
im = im.convert(self.mode)
|
||||
if box:
|
||||
self.image.paste(im.im, box)
|
||||
else:
|
||||
self.image.paste(im.im)
|
||||
|
||||
def frombytes(self, buffer):
|
||||
"""
|
||||
Load display memory contents from byte data.
|
||||
|
||||
:param buffer: A buffer containing display data (usually
|
||||
data returned from :py:func:`~PIL.ImageWin.Dib.tobytes`)
|
||||
"""
|
||||
return self.image.frombytes(buffer)
|
||||
|
||||
def tobytes(self):
|
||||
"""
|
||||
Copy display memory contents to bytes object.
|
||||
|
||||
:return: A bytes object containing display data.
|
||||
"""
|
||||
return self.image.tobytes()
|
||||
|
||||
|
||||
class Window:
|
||||
"""Create a Window with the given title size."""
|
||||
|
||||
def __init__(self, title="PIL", width=None, height=None):
|
||||
self.hwnd = Image.core.createwindow(
|
||||
title, self.__dispatcher, width or 0, height or 0
|
||||
)
|
||||
|
||||
def __dispatcher(self, action, *args):
|
||||
return getattr(self, "ui_handle_" + action)(*args)
|
||||
|
||||
def ui_handle_clear(self, dc, x0, y0, x1, y1):
|
||||
pass
|
||||
|
||||
def ui_handle_damage(self, x0, y0, x1, y1):
|
||||
pass
|
||||
|
||||
def ui_handle_destroy(self):
|
||||
pass
|
||||
|
||||
def ui_handle_repair(self, dc, x0, y0, x1, y1):
|
||||
pass
|
||||
|
||||
def ui_handle_resize(self, width, height):
|
||||
pass
|
||||
|
||||
def mainloop(self):
|
||||
Image.core.eventloop()
|
||||
|
||||
|
||||
class ImageWindow(Window):
|
||||
"""Create an image window which displays the given image."""
|
||||
|
||||
def __init__(self, image, title="PIL"):
|
||||
if not isinstance(image, Dib):
|
||||
image = Dib(image)
|
||||
self.image = image
|
||||
width, height = image.size
|
||||
super().__init__(title, width=width, height=height)
|
||||
|
||||
def ui_handle_repair(self, dc, x0, y0, x1, y1):
|
||||
self.image.draw(dc, (x0, y0, x1, y1))
|
||||
@@ -0,0 +1,92 @@
|
||||
# available_models.py
|
||||
# There isn't an API to do this, sorry
|
||||
available_models = {
|
||||
"llama3":"Meta Llama 3: The most capable openly available LLM to date",
|
||||
"phi3":"Phi-3 Mini is a 3.8B parameters, lightweight, state-of-the-art open model by Microsoft.",
|
||||
"wizardlm2":"State of the art large language model from Microsoft AI with improved performance on complex chat, multilingual, reasoning and agent use cases.",
|
||||
"mistral":"The 7B model released by Mistral AI, updated to version 0.2.",
|
||||
"gemma":"Gemma is a family of lightweight, state-of-the-art open models built by Google DeepMind. Updated to version 1.1",
|
||||
"mixtral":"A set of Mixture of Experts (MoE) model with open weights by Mistral AI in 8x7b and 8x22b parameter sizes.",
|
||||
"llama2":"Llama 2 is a collection of foundation language models ranging from 7B to 70B parameters.",
|
||||
"codegemma":"CodeGemma is a collection of powerful, lightweight models that can perform a variety of coding tasks like fill-in-the-middle code completion, code generation, natural language understanding, mathematical reasoning, and instruction following.",
|
||||
"command-r":"Command R is a Large Language Model optimized for conversational interaction and long context tasks.",
|
||||
"command-r-plus":"Command R+ is a powerful, scalable large language model purpose-built to excel at real-world enterprise use cases.",
|
||||
"llava":"🌋 LLaVA is a novel end-to-end trained large multimodal model that combines a vision encoder and Vicuna for general-purpose visual and language understanding. Updated to version 1.6.",
|
||||
"dbrx":"DBRX is an open, general-purpose LLM created by Databricks.",
|
||||
"codellama":"A large language model that can use text prompts to generate and discuss code.",
|
||||
"qwen":"Qwen 1.5 is a series of large language models by Alibaba Cloud spanning from 0.5B to 110B parameters",
|
||||
"dolphin-mixtral":"Uncensored, 8x7b and 8x22b fine-tuned models based on the Mixtral mixture of experts models that excels at coding tasks. Created by Eric Hartford.",
|
||||
"llama2-uncensored":"Uncensored Llama 2 model by George Sung and Jarrad Hope.",
|
||||
"deepseek-coder":"DeepSeek Coder is a capable coding model trained on two trillion code and natural language tokens.",
|
||||
"mistral-openorca":"Mistral OpenOrca is a 7 billion parameter model, fine-tuned on top of the Mistral 7B model using the OpenOrca dataset.",
|
||||
"nomic-embed-text":"A high-performing open embedding model with a large token context window.",
|
||||
"phi":"Phi-2: a 2.7B language model by Microsoft Research that demonstrates outstanding reasoning and language understanding capabilities.",
|
||||
"dolphin-mistral":"The uncensored Dolphin model based on Mistral that excels at coding tasks. Updated to version 2.8.",
|
||||
"orca-mini":"A general-purpose model ranging from 3 billion parameters to 70 billion, suitable for entry-level hardware.",
|
||||
"nous-hermes2":"The powerful family of models by Nous Research that excels at scientific discussion and coding tasks.",
|
||||
"zephyr":"Zephyr is a series of fine-tuned versions of the Mistral and Mixtral models that are trained to act as helpful assistants.",
|
||||
"llama2-chinese":"Llama 2 based model fine tuned to improve Chinese dialogue ability.",
|
||||
"wizard-vicuna-uncensored":"Wizard Vicuna Uncensored is a 7B, 13B, and 30B parameter model based on Llama 2 uncensored by Eric Hartford.",
|
||||
"vicuna":"General use chat model based on Llama and Llama 2 with 2K to 16K context sizes.",
|
||||
"starcoder2":"StarCoder2 is the next generation of transparently trained open code LLMs that comes in three sizes: 3B, 7B and 15B parameters.",
|
||||
"openhermes":"OpenHermes 2.5 is a 7B model fine-tuned by Teknium on Mistral with fully open datasets.",
|
||||
"tinyllama":"The TinyLlama project is an open endeavor to train a compact 1.1B Llama model on 3 trillion tokens.",
|
||||
"openchat":"A family of open-source models trained on a wide variety of data, surpassing ChatGPT on various benchmarks. Updated to version 3.5-0106.",
|
||||
"tinydolphin":"An experimental 1.1B parameter model trained on the new Dolphin 2.8 dataset by Eric Hartford and based on TinyLlama.",
|
||||
"starcoder":"StarCoder is a code generation model trained on 80+ programming languages.",
|
||||
"wizardcoder":"State-of-the-art code generation model",
|
||||
"stable-code":"Stable Code 3B is a coding model with instruct and code completion variants on par with models such as Code Llama 7B that are 2.5x larger.",
|
||||
"dolphin-llama3":"Dolphin 2.9 is a new model with 8B and 70B sizes by Eric Hartford based on Llama 3 that has a variety of instruction, conversational, and coding skills.",
|
||||
"yi":"A high-performing, bilingual language model.",
|
||||
"mxbai-embed-large":"State-of-the-art large embedding model from mixedbread.ai",
|
||||
"neural-chat":"A fine-tuned model based on Mistral with good coverage of domain and language.",
|
||||
"phind-codellama":"Code generation model based on Code Llama.",
|
||||
"wizard-math":"Model focused on math and logic problems",
|
||||
"starling-lm":"Starling is a large language model trained by reinforcement learning from AI feedback focused on improving chatbot helpfulness.",
|
||||
"falcon":"A large language model built by the Technology Innovation Institute (TII) for use in summarization, text generation, and chat bots.",
|
||||
"orca2":"Orca 2 is built by Microsoft research, and are a fine-tuned version of Meta's Llama 2 models. The model is designed to excel particularly in reasoning.",
|
||||
"dolphincoder":"A 7B and 15B uncensored variant of the Dolphin model family that excels at coding, based on StarCoder2.",
|
||||
"dolphin-phi":"2.7B uncensored Dolphin model by Eric Hartford, based on the Phi language model by Microsoft Research.",
|
||||
"nous-hermes":"General use models based on Llama and Llama 2 from Nous Research.",
|
||||
"sqlcoder":"SQLCoder is a code completion model fined-tuned on StarCoder for SQL generation tasks",
|
||||
"solar":"A compact, yet powerful 10.7B large language model designed for single-turn conversation.",
|
||||
"stablelm2":"Stable LM 2 is a state-of-the-art 1.6B and 12B parameter language model trained on multilingual data in English, Spanish, German, Italian, French, Portuguese, and Dutch.",
|
||||
"bakllava":"BakLLaVA is a multimodal model consisting of the Mistral 7B base model augmented with the LLaVA architecture.",
|
||||
"medllama2":"Fine-tuned Llama 2 model to answer medical questions based on an open source medical dataset.",
|
||||
"yarn-llama2":"An extension of Llama 2 that supports a context of up to 128k tokens.",
|
||||
"deepseek-llm":"An advanced language model crafted with 2 trillion bilingual tokens.",
|
||||
"nous-hermes2-mixtral":"The Nous Hermes 2 model from Nous Research, now trained over Mixtral.",
|
||||
"wizardlm-uncensored":"Uncensored version of Wizard LM model",
|
||||
"codeqwen":"CodeQwen1.5 is a large language model pretrained on a large amount of code data.",
|
||||
"all-minilm":"Embedding models on very large sentence level datasets.",
|
||||
"samantha-mistral":"A companion assistant trained in philosophy, psychology, and personal relationships. Based on Mistral.",
|
||||
"codeup":"Great code generation model based on Llama2.",
|
||||
"stable-beluga":"Llama 2 based model fine tuned on an Orca-style dataset. Originally called Free Willy.",
|
||||
"llama3-gradient":"This model extends LLama-3 8B's context length from 8k to over 1m tokens.",
|
||||
"everythinglm":"Uncensored Llama2 based model with support for a 16K context window.",
|
||||
"xwinlm":"Conversational model based on Llama 2 that performs competitively on various benchmarks.",
|
||||
"yarn-mistral":"An extension of Mistral to support context windows of 64K or 128K.",
|
||||
"meditron":"Open-source medical large language model adapted from Llama 2 to the medical domain.",
|
||||
"wizardlm":"General use model based on Llama 2.",
|
||||
"llama-pro":"An expansion of Llama 2 that specializes in integrating both general language understanding and domain-specific knowledge, particularly in programming and mathematics.",
|
||||
"magicoder":"🎩 Magicoder is a family of 7B parameter models trained on 75K synthetic instruction data using OSS-Instruct, a novel approach to enlightening LLMs with open-source code snippets.",
|
||||
"stablelm-zephyr":"A lightweight chat model allowing accurate, and responsive output without requiring high-end hardware.",
|
||||
"codebooga":"A high-performing code instruct model created by merging two existing code models.",
|
||||
"nexusraven":"Nexus Raven is a 13B instruction tuned model for function calling tasks.",
|
||||
"mistrallite":"MistralLite is a fine-tuned model based on Mistral with enhanced capabilities of processing long contexts.",
|
||||
"wizard-vicuna":"Wizard Vicuna is a 13B parameter model based on Llama 2 trained by MelodysDreamj.",
|
||||
"goliath":"A language model created by combining two fine-tuned Llama 2 70B models into one.",
|
||||
"open-orca-platypus2":"Merge of the Open Orca OpenChat model and the Garage-bAInd Platypus 2 model. Designed for chat and code generation.",
|
||||
"notux":"A top-performing mixture of experts model, fine-tuned with high-quality data.",
|
||||
"megadolphin":"MegaDolphin-2.2-120b is a transformation of Dolphin-2.2-70b created by interleaving the model with itself.",
|
||||
"snowflake-arctic-embed":"A suite of text embedding models by Snowflake, optimized for performance.",
|
||||
"duckdb-nsql":"7B parameter text-to-SQL model made by MotherDuck and Numbers Station.",
|
||||
"moondream":"moondream is a small vision language model designed to run efficiently on edge devices.",
|
||||
"notus":"A 7B chat model fine-tuned with high-quality data and based on Zephyr.",
|
||||
"alfred":"A robust conversational model designed to be used for both chat and instruct use cases.",
|
||||
"llava-llama3":"A LLaVA model fine-tuned from Llama 3 Instruct with better scores in several benchmarks.",
|
||||
"llama3-chatqa":"A model from NVIDIA based on Llama 3 that excels at conversational question answering (QA) and retrieval-augmented generation (RAG).",
|
||||
"llava-phi3":"A new small LLaVA model fine-tuned from Phi 3 Mini."
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
Given a list of integers, made up of (hopefully) a small number of long runs
|
||||
of consecutive integers, compute a representation of the form
|
||||
((start1, end1), (start2, end2) ...). Then answer the question "was x present
|
||||
in the original list?" in time O(log(# runs)).
|
||||
"""
|
||||
|
||||
import bisect
|
||||
from typing import List, Tuple
|
||||
|
||||
def intranges_from_list(list_: List[int]) -> Tuple[int, ...]:
|
||||
"""Represent a list of integers as a sequence of ranges:
|
||||
((start_0, end_0), (start_1, end_1), ...), such that the original
|
||||
integers are exactly those x such that start_i <= x < end_i for some i.
|
||||
|
||||
Ranges are encoded as single integers (start << 32 | end), not as tuples.
|
||||
"""
|
||||
|
||||
sorted_list = sorted(list_)
|
||||
ranges = []
|
||||
last_write = -1
|
||||
for i in range(len(sorted_list)):
|
||||
if i+1 < len(sorted_list):
|
||||
if sorted_list[i] == sorted_list[i+1]-1:
|
||||
continue
|
||||
current_range = sorted_list[last_write+1:i+1]
|
||||
ranges.append(_encode_range(current_range[0], current_range[-1] + 1))
|
||||
last_write = i
|
||||
|
||||
return tuple(ranges)
|
||||
|
||||
def _encode_range(start: int, end: int) -> int:
|
||||
return (start << 32) | end
|
||||
|
||||
def _decode_range(r: int) -> Tuple[int, int]:
|
||||
return (r >> 32), (r & ((1 << 32) - 1))
|
||||
|
||||
|
||||
def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool:
|
||||
"""Determine if `int_` falls into one of the ranges in `ranges`."""
|
||||
tuple_ = _encode_range(int_, 0)
|
||||
pos = bisect.bisect_left(ranges, tuple_)
|
||||
# we could be immediately ahead of a tuple (start, end)
|
||||
# with start < int_ <= end
|
||||
if pos > 0:
|
||||
left, right = _decode_range(ranges[pos-1])
|
||||
if left <= int_ < right:
|
||||
return True
|
||||
# or we could be immediately behind a tuple (int_, end)
|
||||
if pos < len(ranges):
|
||||
left, _ = _decode_range(ranges[pos])
|
||||
if left == int_:
|
||||
return True
|
||||
return False
|
||||
@@ -0,0 +1,227 @@
|
||||
#
|
||||
# The Python Imaging Library.
|
||||
# $Id$
|
||||
#
|
||||
# PCX file handling
|
||||
#
|
||||
# This format was originally used by ZSoft's popular PaintBrush
|
||||
# program for the IBM PC. It is also supported by many MS-DOS and
|
||||
# Windows applications, including the Windows PaintBrush program in
|
||||
# Windows 3.
|
||||
#
|
||||
# history:
|
||||
# 1995-09-01 fl Created
|
||||
# 1996-05-20 fl Fixed RGB support
|
||||
# 1997-01-03 fl Fixed 2-bit and 4-bit support
|
||||
# 1999-02-03 fl Fixed 8-bit support (broken in 1.0b1)
|
||||
# 1999-02-07 fl Added write support
|
||||
# 2002-06-09 fl Made 2-bit and 4-bit support a bit more robust
|
||||
# 2002-07-30 fl Seek from to current position, not beginning of file
|
||||
# 2003-06-03 fl Extract DPI settings (info["dpi"])
|
||||
#
|
||||
# Copyright (c) 1997-2003 by Secret Labs AB.
|
||||
# Copyright (c) 1995-2003 by Fredrik Lundh.
|
||||
#
|
||||
# See the README file for information on usage and redistribution.
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import logging
|
||||
from typing import IO
|
||||
|
||||
from . import Image, ImageFile, ImagePalette
|
||||
from ._binary import i16le as i16
|
||||
from ._binary import o8
|
||||
from ._binary import o16le as o16
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _accept(prefix: bytes) -> bool:
|
||||
return prefix[0] == 10 and prefix[1] in [0, 2, 3, 5]
|
||||
|
||||
|
||||
##
|
||||
# Image plugin for Paintbrush images.
|
||||
|
||||
|
||||
class PcxImageFile(ImageFile.ImageFile):
|
||||
format = "PCX"
|
||||
format_description = "Paintbrush"
|
||||
|
||||
def _open(self) -> None:
|
||||
# header
|
||||
assert self.fp is not None
|
||||
|
||||
s = self.fp.read(128)
|
||||
if not _accept(s):
|
||||
msg = "not a PCX file"
|
||||
raise SyntaxError(msg)
|
||||
|
||||
# image
|
||||
bbox = i16(s, 4), i16(s, 6), i16(s, 8) + 1, i16(s, 10) + 1
|
||||
if bbox[2] <= bbox[0] or bbox[3] <= bbox[1]:
|
||||
msg = "bad PCX image size"
|
||||
raise SyntaxError(msg)
|
||||
logger.debug("BBox: %s %s %s %s", *bbox)
|
||||
|
||||
# format
|
||||
version = s[1]
|
||||
bits = s[3]
|
||||
planes = s[65]
|
||||
provided_stride = i16(s, 66)
|
||||
logger.debug(
|
||||
"PCX version %s, bits %s, planes %s, stride %s",
|
||||
version,
|
||||
bits,
|
||||
planes,
|
||||
provided_stride,
|
||||
)
|
||||
|
||||
self.info["dpi"] = i16(s, 12), i16(s, 14)
|
||||
|
||||
if bits == 1 and planes == 1:
|
||||
mode = rawmode = "1"
|
||||
|
||||
elif bits == 1 and planes in (2, 4):
|
||||
mode = "P"
|
||||
rawmode = "P;%dL" % planes
|
||||
self.palette = ImagePalette.raw("RGB", s[16:64])
|
||||
|
||||
elif version == 5 and bits == 8 and planes == 1:
|
||||
mode = rawmode = "L"
|
||||
# FIXME: hey, this doesn't work with the incremental loader !!!
|
||||
self.fp.seek(-769, io.SEEK_END)
|
||||
s = self.fp.read(769)
|
||||
if len(s) == 769 and s[0] == 12:
|
||||
# check if the palette is linear grayscale
|
||||
for i in range(256):
|
||||
if s[i * 3 + 1 : i * 3 + 4] != o8(i) * 3:
|
||||
mode = rawmode = "P"
|
||||
break
|
||||
if mode == "P":
|
||||
self.palette = ImagePalette.raw("RGB", s[1:])
|
||||
self.fp.seek(128)
|
||||
|
||||
elif version == 5 and bits == 8 and planes == 3:
|
||||
mode = "RGB"
|
||||
rawmode = "RGB;L"
|
||||
|
||||
else:
|
||||
msg = "unknown PCX mode"
|
||||
raise OSError(msg)
|
||||
|
||||
self._mode = mode
|
||||
self._size = bbox[2] - bbox[0], bbox[3] - bbox[1]
|
||||
|
||||
# Don't trust the passed in stride.
|
||||
# Calculate the approximate position for ourselves.
|
||||
# CVE-2020-35653
|
||||
stride = (self._size[0] * bits + 7) // 8
|
||||
|
||||
# While the specification states that this must be even,
|
||||
# not all images follow this
|
||||
if provided_stride != stride:
|
||||
stride += stride % 2
|
||||
|
||||
bbox = (0, 0) + self.size
|
||||
logger.debug("size: %sx%s", *self.size)
|
||||
|
||||
self.tile = [("pcx", bbox, self.fp.tell(), (rawmode, planes * stride))]
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# save PCX files
|
||||
|
||||
|
||||
SAVE = {
|
||||
# mode: (version, bits, planes, raw mode)
|
||||
"1": (2, 1, 1, "1"),
|
||||
"L": (5, 8, 1, "L"),
|
||||
"P": (5, 8, 1, "P"),
|
||||
"RGB": (5, 8, 3, "RGB;L"),
|
||||
}
|
||||
|
||||
|
||||
def _save(im: Image.Image, fp: IO[bytes], filename: str) -> None:
|
||||
try:
|
||||
version, bits, planes, rawmode = SAVE[im.mode]
|
||||
except KeyError as e:
|
||||
msg = f"Cannot save {im.mode} images as PCX"
|
||||
raise ValueError(msg) from e
|
||||
|
||||
# bytes per plane
|
||||
stride = (im.size[0] * bits + 7) // 8
|
||||
# stride should be even
|
||||
stride += stride % 2
|
||||
# Stride needs to be kept in sync with the PcxEncode.c version.
|
||||
# Ideally it should be passed in in the state, but the bytes value
|
||||
# gets overwritten.
|
||||
|
||||
logger.debug(
|
||||
"PcxImagePlugin._save: xwidth: %d, bits: %d, stride: %d",
|
||||
im.size[0],
|
||||
bits,
|
||||
stride,
|
||||
)
|
||||
|
||||
# under windows, we could determine the current screen size with
|
||||
# "Image.core.display_mode()[1]", but I think that's overkill...
|
||||
|
||||
screen = im.size
|
||||
|
||||
dpi = 100, 100
|
||||
|
||||
# PCX header
|
||||
fp.write(
|
||||
o8(10)
|
||||
+ o8(version)
|
||||
+ o8(1)
|
||||
+ o8(bits)
|
||||
+ o16(0)
|
||||
+ o16(0)
|
||||
+ o16(im.size[0] - 1)
|
||||
+ o16(im.size[1] - 1)
|
||||
+ o16(dpi[0])
|
||||
+ o16(dpi[1])
|
||||
+ b"\0" * 24
|
||||
+ b"\xFF" * 24
|
||||
+ b"\0"
|
||||
+ o8(planes)
|
||||
+ o16(stride)
|
||||
+ o16(1)
|
||||
+ o16(screen[0])
|
||||
+ o16(screen[1])
|
||||
+ b"\0" * 54
|
||||
)
|
||||
|
||||
assert fp.tell() == 128
|
||||
|
||||
ImageFile._save(im, fp, [("pcx", (0, 0) + im.size, 0, (rawmode, bits * planes))])
|
||||
|
||||
if im.mode == "P":
|
||||
# colour palette
|
||||
assert im.im is not None
|
||||
|
||||
fp.write(o8(12))
|
||||
palette = im.im.getpalette("RGB", "RGB")
|
||||
palette += b"\x00" * (768 - len(palette))
|
||||
fp.write(palette) # 768 bytes
|
||||
elif im.mode == "L":
|
||||
# grayscale palette
|
||||
fp.write(o8(12))
|
||||
for i in range(256):
|
||||
fp.write(o8(i) * 3)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# registry
|
||||
|
||||
|
||||
Image.register_open(PcxImageFile.format, PcxImageFile, _accept)
|
||||
Image.register_save(PcxImageFile.format, _save)
|
||||
|
||||
Image.register_extension(PcxImageFile.format, ".pcx")
|
||||
|
||||
Image.register_mime(PcxImageFile.format, "image/x-pcx")
|
||||
Reference in New Issue
Block a user