InvokeAI 6.10.0rc1__py3-none-any.whl → 6.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- invokeai/app/api/routers/model_manager.py +43 -1
- invokeai/app/invocations/fields.py +1 -1
- invokeai/app/invocations/flux2_denoise.py +499 -0
- invokeai/app/invocations/flux2_klein_model_loader.py +222 -0
- invokeai/app/invocations/flux2_klein_text_encoder.py +222 -0
- invokeai/app/invocations/flux2_vae_decode.py +106 -0
- invokeai/app/invocations/flux2_vae_encode.py +88 -0
- invokeai/app/invocations/flux_denoise.py +77 -3
- invokeai/app/invocations/flux_lora_loader.py +1 -1
- invokeai/app/invocations/flux_model_loader.py +2 -5
- invokeai/app/invocations/ideal_size.py +6 -1
- invokeai/app/invocations/metadata.py +4 -0
- invokeai/app/invocations/metadata_linked.py +47 -0
- invokeai/app/invocations/model.py +1 -0
- invokeai/app/invocations/pbr_maps.py +59 -0
- invokeai/app/invocations/z_image_denoise.py +244 -84
- invokeai/app/invocations/z_image_image_to_latents.py +9 -1
- invokeai/app/invocations/z_image_latents_to_image.py +9 -1
- invokeai/app/invocations/z_image_seed_variance_enhancer.py +110 -0
- invokeai/app/services/config/config_default.py +3 -1
- invokeai/app/services/invocation_stats/invocation_stats_common.py +6 -6
- invokeai/app/services/invocation_stats/invocation_stats_default.py +9 -4
- invokeai/app/services/model_manager/model_manager_default.py +7 -0
- invokeai/app/services/model_records/model_records_base.py +4 -2
- invokeai/app/services/shared/invocation_context.py +15 -0
- invokeai/app/services/shared/sqlite/sqlite_util.py +2 -0
- invokeai/app/services/shared/sqlite_migrator/migrations/migration_25.py +61 -0
- invokeai/app/util/step_callback.py +58 -2
- invokeai/backend/flux/denoise.py +338 -118
- invokeai/backend/flux/dype/__init__.py +31 -0
- invokeai/backend/flux/dype/base.py +260 -0
- invokeai/backend/flux/dype/embed.py +116 -0
- invokeai/backend/flux/dype/presets.py +148 -0
- invokeai/backend/flux/dype/rope.py +110 -0
- invokeai/backend/flux/extensions/dype_extension.py +91 -0
- invokeai/backend/flux/schedulers.py +62 -0
- invokeai/backend/flux/util.py +35 -1
- invokeai/backend/flux2/__init__.py +4 -0
- invokeai/backend/flux2/denoise.py +280 -0
- invokeai/backend/flux2/ref_image_extension.py +294 -0
- invokeai/backend/flux2/sampling_utils.py +209 -0
- invokeai/backend/image_util/pbr_maps/architecture/block.py +367 -0
- invokeai/backend/image_util/pbr_maps/architecture/pbr_rrdb_net.py +70 -0
- invokeai/backend/image_util/pbr_maps/pbr_maps.py +141 -0
- invokeai/backend/image_util/pbr_maps/utils/image_ops.py +93 -0
- invokeai/backend/model_manager/configs/factory.py +19 -1
- invokeai/backend/model_manager/configs/lora.py +36 -0
- invokeai/backend/model_manager/configs/main.py +395 -3
- invokeai/backend/model_manager/configs/qwen3_encoder.py +116 -7
- invokeai/backend/model_manager/configs/vae.py +104 -2
- invokeai/backend/model_manager/load/model_cache/model_cache.py +107 -2
- invokeai/backend/model_manager/load/model_loaders/cogview4.py +2 -1
- invokeai/backend/model_manager/load/model_loaders/flux.py +1020 -8
- invokeai/backend/model_manager/load/model_loaders/generic_diffusers.py +4 -2
- invokeai/backend/model_manager/load/model_loaders/onnx.py +1 -0
- invokeai/backend/model_manager/load/model_loaders/stable_diffusion.py +2 -1
- invokeai/backend/model_manager/load/model_loaders/z_image.py +158 -31
- invokeai/backend/model_manager/starter_models.py +141 -4
- invokeai/backend/model_manager/taxonomy.py +31 -4
- invokeai/backend/model_manager/util/select_hf_files.py +3 -2
- invokeai/backend/patches/lora_conversions/z_image_lora_conversion_utils.py +39 -5
- invokeai/backend/quantization/gguf/ggml_tensor.py +15 -4
- invokeai/backend/util/vae_working_memory.py +0 -2
- invokeai/backend/z_image/extensions/regional_prompting_extension.py +10 -12
- invokeai/frontend/web/dist/assets/App-D13dX7be.js +161 -0
- invokeai/frontend/web/dist/assets/{browser-ponyfill-DHZxq1nk.js → browser-ponyfill-u_ZjhQTI.js} +1 -1
- invokeai/frontend/web/dist/assets/index-BB0nHmDe.js +530 -0
- invokeai/frontend/web/dist/index.html +1 -1
- invokeai/frontend/web/dist/locales/en-GB.json +1 -0
- invokeai/frontend/web/dist/locales/en.json +85 -6
- invokeai/frontend/web/dist/locales/it.json +135 -15
- invokeai/frontend/web/dist/locales/ru.json +11 -11
- invokeai/version/invokeai_version.py +1 -1
- {invokeai-6.10.0rc1.dist-info → invokeai-6.11.0.dist-info}/METADATA +8 -2
- {invokeai-6.10.0rc1.dist-info → invokeai-6.11.0.dist-info}/RECORD +81 -57
- {invokeai-6.10.0rc1.dist-info → invokeai-6.11.0.dist-info}/WHEEL +1 -1
- invokeai/frontend/web/dist/assets/App-CYhlZO3Q.js +0 -161
- invokeai/frontend/web/dist/assets/index-dgSJAY--.js +0 -530
- {invokeai-6.10.0rc1.dist-info → invokeai-6.11.0.dist-info}/entry_points.txt +0 -0
- {invokeai-6.10.0rc1.dist-info → invokeai-6.11.0.dist-info}/licenses/LICENSE +0 -0
- {invokeai-6.10.0rc1.dist-info → invokeai-6.11.0.dist-info}/licenses/LICENSE-SD1+SD2.txt +0 -0
- {invokeai-6.10.0rc1.dist-info → invokeai-6.11.0.dist-info}/licenses/LICENSE-SDXL.txt +0 -0
- {invokeai-6.10.0rc1.dist-info → invokeai-6.11.0.dist-info}/top_level.txt +0 -0
|
@@ -52,8 +52,9 @@ class InvocationStatsService(InvocationStatsServiceBase):
|
|
|
52
52
|
# Record state before the invocation.
|
|
53
53
|
start_time = time.time()
|
|
54
54
|
start_ram = psutil.Process().memory_info().rss
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
|
|
56
|
+
# Remember current VRAM usage
|
|
57
|
+
vram_in_use = torch.cuda.memory_allocated() if torch.cuda.is_available() else 0.0
|
|
57
58
|
|
|
58
59
|
assert services.model_manager.load is not None
|
|
59
60
|
services.model_manager.load.ram_cache.stats = self._cache_stats[graph_execution_state_id]
|
|
@@ -62,14 +63,16 @@ class InvocationStatsService(InvocationStatsServiceBase):
|
|
|
62
63
|
# Let the invocation run.
|
|
63
64
|
yield None
|
|
64
65
|
finally:
|
|
65
|
-
# Record
|
|
66
|
+
# Record delta VRAM
|
|
67
|
+
delta_vram_gb = ((torch.cuda.memory_allocated() - vram_in_use) / GB) if torch.cuda.is_available() else 0.0
|
|
68
|
+
|
|
66
69
|
node_stats = NodeExecutionStats(
|
|
67
70
|
invocation_type=invocation.get_type(),
|
|
68
71
|
start_time=start_time,
|
|
69
72
|
end_time=time.time(),
|
|
70
73
|
start_ram_gb=start_ram / GB,
|
|
71
74
|
end_ram_gb=psutil.Process().memory_info().rss / GB,
|
|
72
|
-
|
|
75
|
+
delta_vram_gb=delta_vram_gb,
|
|
73
76
|
)
|
|
74
77
|
self._stats[graph_execution_state_id].add_node_execution_stats(node_stats)
|
|
75
78
|
|
|
@@ -81,6 +84,8 @@ class InvocationStatsService(InvocationStatsServiceBase):
|
|
|
81
84
|
graph_stats_summary = self._get_graph_summary(graph_execution_state_id)
|
|
82
85
|
node_stats_summaries = self._get_node_summaries(graph_execution_state_id)
|
|
83
86
|
model_cache_stats_summary = self._get_model_cache_summary(graph_execution_state_id)
|
|
87
|
+
# Note: We use memory_allocated() here (not memory_reserved()) because we want to show
|
|
88
|
+
# the current actively-used VRAM, not the total reserved memory including PyTorch's cache.
|
|
84
89
|
vram_usage_gb = torch.cuda.memory_allocated() / GB if torch.cuda.is_available() else None
|
|
85
90
|
|
|
86
91
|
return InvocationStatsSummary(
|
|
@@ -60,6 +60,10 @@ class ModelManagerService(ModelManagerServiceBase):
|
|
|
60
60
|
service.start(invoker)
|
|
61
61
|
|
|
62
62
|
def stop(self, invoker: Invoker) -> None:
|
|
63
|
+
# Shutdown the model cache to cancel any pending timers
|
|
64
|
+
if hasattr(self._load, "ram_cache"):
|
|
65
|
+
self._load.ram_cache.shutdown()
|
|
66
|
+
|
|
63
67
|
for service in [self._store, self._install, self._load]:
|
|
64
68
|
if hasattr(service, "stop"):
|
|
65
69
|
service.stop(invoker)
|
|
@@ -88,7 +92,10 @@ class ModelManagerService(ModelManagerServiceBase):
|
|
|
88
92
|
max_ram_cache_size_gb=app_config.max_cache_ram_gb,
|
|
89
93
|
max_vram_cache_size_gb=app_config.max_cache_vram_gb,
|
|
90
94
|
execution_device=execution_device or TorchDevice.choose_torch_device(),
|
|
95
|
+
storage_device="cpu",
|
|
96
|
+
log_memory_usage=app_config.log_memory_usage,
|
|
91
97
|
logger=logger,
|
|
98
|
+
keep_alive_minutes=app_config.model_cache_keep_alive_min,
|
|
92
99
|
)
|
|
93
100
|
loader = ModelLoadService(
|
|
94
101
|
app_config=app_config,
|
|
@@ -19,11 +19,13 @@ from invokeai.backend.model_manager.configs.main import MainModelDefaultSettings
|
|
|
19
19
|
from invokeai.backend.model_manager.taxonomy import (
|
|
20
20
|
BaseModelType,
|
|
21
21
|
ClipVariantType,
|
|
22
|
+
Flux2VariantType,
|
|
22
23
|
FluxVariantType,
|
|
23
24
|
ModelFormat,
|
|
24
25
|
ModelSourceType,
|
|
25
26
|
ModelType,
|
|
26
27
|
ModelVariantType,
|
|
28
|
+
Qwen3VariantType,
|
|
27
29
|
SchedulerPredictionType,
|
|
28
30
|
)
|
|
29
31
|
|
|
@@ -89,8 +91,8 @@ class ModelRecordChanges(BaseModelExcludeNull):
|
|
|
89
91
|
|
|
90
92
|
# Checkpoint-specific changes
|
|
91
93
|
# TODO(MM2): Should we expose these? Feels footgun-y...
|
|
92
|
-
variant: Optional[ModelVariantType | ClipVariantType | FluxVariantType] =
|
|
93
|
-
description="The variant of the model.", default=None
|
|
94
|
+
variant: Optional[ModelVariantType | ClipVariantType | FluxVariantType | Flux2VariantType | Qwen3VariantType] = (
|
|
95
|
+
Field(description="The variant of the model.", default=None)
|
|
94
96
|
)
|
|
95
97
|
prediction_type: Optional[SchedulerPredictionType] = Field(
|
|
96
98
|
description="The prediction type of the model.", default=None
|
|
@@ -630,6 +630,21 @@ class UtilInterface(InvocationContextInterface):
|
|
|
630
630
|
is_canceled=self.is_canceled,
|
|
631
631
|
)
|
|
632
632
|
|
|
633
|
+
def flux2_step_callback(self, intermediate_state: PipelineIntermediateState) -> None:
|
|
634
|
+
"""
|
|
635
|
+
The step callback for FLUX.2 Klein models (32-channel VAE).
|
|
636
|
+
|
|
637
|
+
Args:
|
|
638
|
+
intermediate_state: The intermediate state of the diffusion pipeline.
|
|
639
|
+
"""
|
|
640
|
+
|
|
641
|
+
diffusion_step_callback(
|
|
642
|
+
signal_progress=self.signal_progress,
|
|
643
|
+
intermediate_state=intermediate_state,
|
|
644
|
+
base_model=BaseModelType.Flux2,
|
|
645
|
+
is_canceled=self.is_canceled,
|
|
646
|
+
)
|
|
647
|
+
|
|
633
648
|
def signal_progress(
|
|
634
649
|
self,
|
|
635
650
|
message: str,
|
|
@@ -27,6 +27,7 @@ from invokeai.app.services.shared.sqlite_migrator.migrations.migration_21 import
|
|
|
27
27
|
from invokeai.app.services.shared.sqlite_migrator.migrations.migration_22 import build_migration_22
|
|
28
28
|
from invokeai.app.services.shared.sqlite_migrator.migrations.migration_23 import build_migration_23
|
|
29
29
|
from invokeai.app.services.shared.sqlite_migrator.migrations.migration_24 import build_migration_24
|
|
30
|
+
from invokeai.app.services.shared.sqlite_migrator.migrations.migration_25 import build_migration_25
|
|
30
31
|
from invokeai.app.services.shared.sqlite_migrator.sqlite_migrator_impl import SqliteMigrator
|
|
31
32
|
|
|
32
33
|
|
|
@@ -71,6 +72,7 @@ def init_db(config: InvokeAIAppConfig, logger: Logger, image_files: ImageFileSto
|
|
|
71
72
|
migrator.register_migration(build_migration_22(app_config=config, logger=logger))
|
|
72
73
|
migrator.register_migration(build_migration_23(app_config=config, logger=logger))
|
|
73
74
|
migrator.register_migration(build_migration_24(app_config=config, logger=logger))
|
|
75
|
+
migrator.register_migration(build_migration_25(app_config=config, logger=logger))
|
|
74
76
|
migrator.run_migrations()
|
|
75
77
|
|
|
76
78
|
return db
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sqlite3
|
|
3
|
+
from logging import Logger
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from invokeai.app.services.config import InvokeAIAppConfig
|
|
7
|
+
from invokeai.app.services.shared.sqlite_migrator.sqlite_migrator_common import Migration
|
|
8
|
+
from invokeai.backend.model_manager.taxonomy import ModelType, Qwen3VariantType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Migration25Callback:
|
|
12
|
+
def __init__(self, app_config: InvokeAIAppConfig, logger: Logger) -> None:
|
|
13
|
+
self._app_config = app_config
|
|
14
|
+
self._logger = logger
|
|
15
|
+
|
|
16
|
+
def __call__(self, cursor: sqlite3.Cursor) -> None:
|
|
17
|
+
cursor.execute("SELECT id, config FROM models;")
|
|
18
|
+
rows = cursor.fetchall()
|
|
19
|
+
|
|
20
|
+
migrated_count = 0
|
|
21
|
+
|
|
22
|
+
for model_id, config_json in rows:
|
|
23
|
+
try:
|
|
24
|
+
config_dict: dict[str, Any] = json.loads(config_json)
|
|
25
|
+
|
|
26
|
+
if config_dict.get("type") != ModelType.Qwen3Encoder.value:
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
if "variant" in config_dict:
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
config_dict["variant"] = Qwen3VariantType.Qwen3_4B.value
|
|
33
|
+
|
|
34
|
+
cursor.execute(
|
|
35
|
+
"UPDATE models SET config = ? WHERE id = ?;",
|
|
36
|
+
(json.dumps(config_dict), model_id),
|
|
37
|
+
)
|
|
38
|
+
migrated_count += 1
|
|
39
|
+
|
|
40
|
+
except json.JSONDecodeError as e:
|
|
41
|
+
self._logger.error("Invalid config JSON for model %s: %s", model_id, e)
|
|
42
|
+
raise
|
|
43
|
+
|
|
44
|
+
if migrated_count > 0:
|
|
45
|
+
self._logger.info(f"Migration complete: {migrated_count} Qwen3 encoder configs updated with variant field")
|
|
46
|
+
else:
|
|
47
|
+
self._logger.info("Migration complete: no Qwen3 encoder configs needed migration")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def build_migration_25(app_config: InvokeAIAppConfig, logger: Logger) -> Migration:
|
|
51
|
+
"""Builds the migration object for migrating from version 24 to version 25.
|
|
52
|
+
|
|
53
|
+
This migration adds the variant field to existing Qwen3 encoder models.
|
|
54
|
+
Models installed before the variant field was added will default to Qwen3_4B (for Z-Image compatibility).
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
return Migration(
|
|
58
|
+
from_version=24,
|
|
59
|
+
to_version=25,
|
|
60
|
+
callback=Migration25Callback(app_config=app_config, logger=logger),
|
|
61
|
+
)
|
|
@@ -93,14 +93,60 @@ COGVIEW4_LATENT_RGB_FACTORS = [
|
|
|
93
93
|
[-0.00955853, -0.00980067, -0.00977842],
|
|
94
94
|
]
|
|
95
95
|
|
|
96
|
+
# FLUX.2 uses 32 latent channels.
|
|
97
|
+
# Factors from ComfyUI: https://github.com/Comfy-Org/ComfyUI/blob/main/comfy/latent_formats.py
|
|
98
|
+
FLUX2_LATENT_RGB_FACTORS = [
|
|
99
|
+
# R G B
|
|
100
|
+
[0.0058, 0.0113, 0.0073],
|
|
101
|
+
[0.0495, 0.0443, 0.0836],
|
|
102
|
+
[-0.0099, 0.0096, 0.0644],
|
|
103
|
+
[0.2144, 0.3009, 0.3652],
|
|
104
|
+
[0.0166, -0.0039, -0.0054],
|
|
105
|
+
[0.0157, 0.0103, -0.0160],
|
|
106
|
+
[-0.0398, 0.0902, -0.0235],
|
|
107
|
+
[-0.0052, 0.0095, 0.0109],
|
|
108
|
+
[-0.3527, -0.2712, -0.1666],
|
|
109
|
+
[-0.0301, -0.0356, -0.0180],
|
|
110
|
+
[-0.0107, 0.0078, 0.0013],
|
|
111
|
+
[0.0746, 0.0090, -0.0941],
|
|
112
|
+
[0.0156, 0.0169, 0.0070],
|
|
113
|
+
[-0.0034, -0.0040, -0.0114],
|
|
114
|
+
[0.0032, 0.0181, 0.0080],
|
|
115
|
+
[-0.0939, -0.0008, 0.0186],
|
|
116
|
+
[0.0018, 0.0043, 0.0104],
|
|
117
|
+
[0.0284, 0.0056, -0.0127],
|
|
118
|
+
[-0.0024, -0.0022, -0.0030],
|
|
119
|
+
[0.1207, -0.0026, 0.0065],
|
|
120
|
+
[0.0128, 0.0101, 0.0142],
|
|
121
|
+
[0.0137, -0.0072, -0.0007],
|
|
122
|
+
[0.0095, 0.0092, -0.0059],
|
|
123
|
+
[0.0000, -0.0077, -0.0049],
|
|
124
|
+
[-0.0465, -0.0204, -0.0312],
|
|
125
|
+
[0.0095, 0.0012, -0.0066],
|
|
126
|
+
[0.0290, -0.0034, 0.0025],
|
|
127
|
+
[0.0220, 0.0169, -0.0048],
|
|
128
|
+
[-0.0332, -0.0457, -0.0468],
|
|
129
|
+
[-0.0085, 0.0389, 0.0609],
|
|
130
|
+
[-0.0076, 0.0003, -0.0043],
|
|
131
|
+
[-0.0111, -0.0460, -0.0614],
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
FLUX2_LATENT_RGB_BIAS = [-0.0329, -0.0718, -0.0851]
|
|
135
|
+
|
|
96
136
|
|
|
97
137
|
def sample_to_lowres_estimated_image(
|
|
98
|
-
samples: torch.Tensor,
|
|
138
|
+
samples: torch.Tensor,
|
|
139
|
+
latent_rgb_factors: torch.Tensor,
|
|
140
|
+
smooth_matrix: Optional[torch.Tensor] = None,
|
|
141
|
+
latent_rgb_bias: Optional[torch.Tensor] = None,
|
|
99
142
|
):
|
|
100
143
|
if samples.dim() == 4:
|
|
101
144
|
samples = samples[0]
|
|
102
145
|
latent_image = samples.permute(1, 2, 0) @ latent_rgb_factors
|
|
103
146
|
|
|
147
|
+
if latent_rgb_bias is not None:
|
|
148
|
+
latent_image = latent_image + latent_rgb_bias
|
|
149
|
+
|
|
104
150
|
if smooth_matrix is not None:
|
|
105
151
|
latent_image = latent_image.unsqueeze(0).permute(3, 0, 1, 2)
|
|
106
152
|
latent_image = torch.nn.functional.conv2d(latent_image, smooth_matrix.reshape((1, 1, 3, 3)), padding=1)
|
|
@@ -153,6 +199,7 @@ def diffusion_step_callback(
|
|
|
153
199
|
sample = intermediate_state.latents
|
|
154
200
|
|
|
155
201
|
smooth_matrix: list[list[float]] | None = None
|
|
202
|
+
latent_rgb_bias: list[float] | None = None
|
|
156
203
|
if base_model in [BaseModelType.StableDiffusion1, BaseModelType.StableDiffusion2]:
|
|
157
204
|
latent_rgb_factors = SD1_5_LATENT_RGB_FACTORS
|
|
158
205
|
elif base_model in [BaseModelType.StableDiffusionXL, BaseModelType.StableDiffusionXLRefiner]:
|
|
@@ -164,6 +211,9 @@ def diffusion_step_callback(
|
|
|
164
211
|
latent_rgb_factors = COGVIEW4_LATENT_RGB_FACTORS
|
|
165
212
|
elif base_model == BaseModelType.Flux:
|
|
166
213
|
latent_rgb_factors = FLUX_LATENT_RGB_FACTORS
|
|
214
|
+
elif base_model == BaseModelType.Flux2:
|
|
215
|
+
latent_rgb_factors = FLUX2_LATENT_RGB_FACTORS
|
|
216
|
+
latent_rgb_bias = FLUX2_LATENT_RGB_BIAS
|
|
167
217
|
elif base_model == BaseModelType.ZImage:
|
|
168
218
|
# Z-Image uses FLUX-compatible VAE with 16 latent channels
|
|
169
219
|
latent_rgb_factors = FLUX_LATENT_RGB_FACTORS
|
|
@@ -174,8 +224,14 @@ def diffusion_step_callback(
|
|
|
174
224
|
smooth_matrix_torch = (
|
|
175
225
|
torch.tensor(smooth_matrix, dtype=sample.dtype, device=sample.device) if smooth_matrix else None
|
|
176
226
|
)
|
|
227
|
+
latent_rgb_bias_torch = (
|
|
228
|
+
torch.tensor(latent_rgb_bias, dtype=sample.dtype, device=sample.device) if latent_rgb_bias else None
|
|
229
|
+
)
|
|
177
230
|
image = sample_to_lowres_estimated_image(
|
|
178
|
-
samples=sample,
|
|
231
|
+
samples=sample,
|
|
232
|
+
latent_rgb_factors=latent_rgb_factors_torch,
|
|
233
|
+
smooth_matrix=smooth_matrix_torch,
|
|
234
|
+
latent_rgb_bias=latent_rgb_bias_torch,
|
|
179
235
|
)
|
|
180
236
|
|
|
181
237
|
width = image.width * 8
|