88
99
1010ASPECT_RATIO_TOLERANCE = 0.01
11+ CALCULATION_CONFIG_VERSION = 1
12+ CALCULATION_CONFIG_VERSION_KEY = "__resolutionMasterConfigVersion"
13+ CALCULATION_PROFILE_KEY = "__resolutionMasterProfile"
14+ CALCULATION_PRESETS_KEY = "__resolutionMasterPresets"
15+
16+
17+ LEGACY_MODEL_PROFILES = {
18+ "Standard" : {"strategy" : "closest_aspect" },
19+ "SDXL" : {"strategy" : "closest_preset" },
20+ "Flux" : {
21+ "strategy" : "flux_like" ,
22+ "options" : {
23+ "max_megapixels" : 4.0 ,
24+ "max_dimension" : 2560 ,
25+ "min_dimension" : 320 ,
26+ "multiple" : 32 ,
27+ },
28+ },
29+ "Flux.2" : {
30+ "strategy" : "flux_like" ,
31+ "options" : {
32+ "max_megapixels" : 6.0 ,
33+ "max_dimension" : 3840 ,
34+ "min_dimension" : 320 ,
35+ "multiple" : 16 ,
36+ },
37+ },
38+ "WAN" : {
39+ "strategy" : "wan_pixel_range" ,
40+ "options" : {"min_pixels" : 182080 , "max_pixels" : 1195560 , "multiple" : 16 },
41+ },
42+ "HiDream Dev" : {"strategy" : "closest_preset" },
43+ "Qwen-Image" : {
44+ "strategy" : "pixel_range" ,
45+ "options" : {"min_pixels" : 589824 , "max_pixels" : 4194304 },
46+ },
47+ "ZImageTurbo" : {"strategy" : "closest_preset" },
48+ "Krea 2 Turbo" : {"strategy" : "closest_preset" },
49+ "Krea 2 RAW" : {"strategy" : "closest_preset" },
50+ "Social Media" : {"strategy" : "closest_aspect" },
51+ "Print" : {"strategy" : "closest_aspect" },
52+ "Cinema" : {"strategy" : "closest_aspect" },
53+ "Display Resolutions" : {"strategy" : "closest_aspect" },
54+ }
1155
1256
1357def safe_int (value , default = 0 ):
@@ -24,16 +68,32 @@ def safe_float(value, default=0.0):
2468 return default
2569
2670
27- def load_presets (presets_json ):
71+ def load_calculation_config (presets_json ):
2872 try :
29- presets = json .loads (presets_json or "{}" )
30- if isinstance (presets , dict ):
31- return presets
32- log .warning ("Ignoring presets JSON because decoded value is not an object" )
33- return {}
73+ decoded = json .loads (presets_json or "{}" )
74+ if not isinstance (decoded , dict ):
75+ log .warning ("Ignoring presets JSON because decoded value is not an object" )
76+ return {}, {}
77+
78+ if decoded .get (CALCULATION_CONFIG_VERSION_KEY ) == CALCULATION_CONFIG_VERSION :
79+ presets = decoded .get (CALCULATION_PRESETS_KEY )
80+ profile = decoded .get (CALCULATION_PROFILE_KEY )
81+ if not isinstance (presets , dict ):
82+ log .warning ("Ignoring calculation presets because config value is not an object" )
83+ presets = {}
84+ if not isinstance (profile , dict ):
85+ profile = {}
86+ return presets , profile
87+
88+ return decoded , {}
3489 except (TypeError , ValueError ) as error :
3590 log .warning ("Failed to parse presets JSON" , error )
36- return {}
91+ return {}, {}
92+
93+
94+ def load_presets (presets_json ):
95+ presets , _profile = load_calculation_config (presets_json )
96+ return presets
3797
3898
3999def choose_best_scaling_option (current_pixels , option1 , option2 ):
@@ -138,51 +198,97 @@ def apply_flux_like_calculation(width, height, max_mp, max_dim, min_dim, multipl
138198 }
139199
140200
141- def apply_custom_calculation (width , height , category , presets ):
142- if category == "Flux" :
143- return apply_flux_like_calculation (width , height , 4.0 , 2560 , 320 , 32 )
144- if category == "Flux.2" :
145- return apply_flux_like_calculation (width , height , 6.0 , 3840 , 320 , 16 )
146- if category == "WAN" :
147- target_pixels = max (182080 , min (1195560 , width * height ))
148- aspect = width / height
149- target_height = math .sqrt (target_pixels / aspect )
150- target_width = target_height * aspect
151- return {
152- "width" : round (target_width / 16 ) * 16 ,
153- "height" : round (target_height / 16 ) * 16 ,
154- }
155- if category == "Qwen-Image" :
156- current_pixels = width * height
157- min_pixels = 589824
158- max_pixels = 4194304
159- if min_pixels <= current_pixels <= max_pixels :
160- return {"width" : width , "height" : height }
161- target_pixels = min_pixels if current_pixels < min_pixels else max_pixels
162- aspect = width / height
163- target_height = math .sqrt (target_pixels / aspect )
164- return {"width" : round (target_height * aspect ), "height" : round (target_height )}
165-
166- if category in ("SDXL" , "HiDream Dev" , "Krea 2 Turbo" , "Krea 2 RAW" , "ZImageTurbo" ):
167- closest = find_closest_preset (width , height , presets )
168- if not closest :
169- return {"width" : width , "height" : height }
170- return {"width" : closest ["width" ], "height" : closest ["height" ]}
171-
172- if category not in ("Standard" , "Social Media" , "Print" , "Cinema" , "Display Resolutions" ):
173- return {"width" : width , "height" : height }
201+ def _unchanged_dimensions (width , height ):
202+ return {"width" : width , "height" : height }
203+
204+
205+ def _apply_closest_preset_strategy (width , height , presets , _options ):
206+ closest = find_closest_preset (width , height , presets )
207+ if not closest :
208+ return _unchanged_dimensions (width , height )
209+ return {"width" : closest ["width" ], "height" : closest ["height" ]}
210+
174211
212+ def _apply_closest_aspect_strategy (width , height , presets , options ):
175213 closest = find_closest_preset (width , height , presets )
176214 if not closest :
177- return { " width" : width , " height" : height }
215+ return _unchanged_dimensions ( width , height )
178216
179217 preset_aspect = closest ["width" ] / closest ["height" ]
180218 current_aspect = width / height
181- if abs (current_aspect - preset_aspect ) < 0.01 :
182- return {"width" : width , "height" : height }
219+ tolerance = max (0.0 , safe_float (options .get ("tolerance" ), ASPECT_RATIO_TOLERANCE ))
220+ if abs (current_aspect - preset_aspect ) < tolerance :
221+ return _unchanged_dimensions (width , height )
183222 return scale_to_preset_aspect_ratio (width , height , preset_aspect )
184223
185224
225+ def _apply_flux_like_strategy (width , height , _presets , options ):
226+ min_dimension = max (1 , safe_int (options .get ("min_dimension" ), 320 ))
227+ max_dimension = max (min_dimension , safe_int (options .get ("max_dimension" ), 2560 ))
228+ return apply_flux_like_calculation (
229+ width ,
230+ height ,
231+ max (0.001 , safe_float (options .get ("max_megapixels" ), 4.0 )),
232+ max_dimension ,
233+ min_dimension ,
234+ max (1 , safe_int (options .get ("multiple" ), 32 )),
235+ )
236+
237+
238+ def _apply_wan_pixel_range_strategy (width , height , _presets , options ):
239+ min_pixels = max (1 , safe_int (options .get ("min_pixels" ), 182080 ))
240+ max_pixels = max (min_pixels , safe_int (options .get ("max_pixels" ), 1195560 ))
241+ multiple = max (1 , safe_int (options .get ("multiple" ), 16 ))
242+ target_pixels = max (min_pixels , min (max_pixels , width * height ))
243+ aspect = width / height
244+ target_height = math .sqrt (target_pixels / aspect )
245+ target_width = target_height * aspect
246+ return {
247+ "width" : max (multiple , round (target_width / multiple ) * multiple ),
248+ "height" : max (multiple , round (target_height / multiple ) * multiple ),
249+ }
250+
251+
252+ def _apply_pixel_range_strategy (width , height , _presets , options ):
253+ current_pixels = width * height
254+ min_pixels = max (1 , safe_int (options .get ("min_pixels" ), 589824 ))
255+ max_pixels = max (min_pixels , safe_int (options .get ("max_pixels" ), 4194304 ))
256+ if min_pixels <= current_pixels <= max_pixels :
257+ return _unchanged_dimensions (width , height )
258+ target_pixels = min_pixels if current_pixels < min_pixels else max_pixels
259+ aspect = width / height
260+ target_height = math .sqrt (target_pixels / aspect )
261+ return {"width" : round (target_height * aspect ), "height" : round (target_height )}
262+
263+
264+ CALCULATION_STRATEGIES = {
265+ "closest_aspect" : _apply_closest_aspect_strategy ,
266+ "closest_preset" : _apply_closest_preset_strategy ,
267+ "flux_like" : _apply_flux_like_strategy ,
268+ "pixel_range" : _apply_pixel_range_strategy ,
269+ "wan_pixel_range" : _apply_wan_pixel_range_strategy ,
270+ }
271+
272+
273+ def apply_custom_calculation (width , height , category , presets , profile = None ):
274+ resolved_profile = profile if isinstance (profile , dict ) and profile .get ("strategy" ) else None
275+ if resolved_profile is None :
276+ resolved_profile = LEGACY_MODEL_PROFILES .get (category )
277+ if not resolved_profile :
278+ return _unchanged_dimensions (width , height )
279+
280+ strategy_name = resolved_profile .get ("strategy" )
281+ strategy = CALCULATION_STRATEGIES .get (strategy_name )
282+ if strategy is None :
283+ log .warning ("Unknown calculation strategy" , strategy_name , "for category" , category )
284+ return _unchanged_dimensions (width , height )
285+
286+ options = resolved_profile .get ("options" )
287+ if not isinstance (options , dict ):
288+ options = {}
289+ return strategy (width , height , presets , options )
290+
291+
186292def calculate_auto_fit (width , height , category , smart_fit , presets , preserve_scaling_ratio = False ):
187293 closest = find_closest_preset (width , height , presets )
188294 if not closest :
@@ -326,7 +432,7 @@ def calculate_resolution(
326432 target_resolution = max (1 , safe_int (target_resolution , 1080 ))
327433 target_megapixels = max (0.0 , safe_float (target_megapixels , 2.0 ))
328434 selected_category = selected_category or ""
329- presets = load_presets (presets_json )
435+ presets , calculation_profile = load_calculation_config (presets_json )
330436 selected_preset = None
331437
332438 log .debug (
@@ -363,7 +469,13 @@ def calculate_resolution(
363469
364470 elif action == "custom_calc" :
365471 if selected_category :
366- calculated = apply_custom_calculation (width , height , selected_category , presets )
472+ calculated = apply_custom_calculation (
473+ width ,
474+ height ,
475+ selected_category ,
476+ presets ,
477+ calculation_profile ,
478+ )
367479 width , height = calculated ["width" ], calculated ["height" ]
368480
369481 elif action == "auto_detect" :
@@ -389,7 +501,13 @@ def calculate_resolution(
389501 width , height = snapped ["width" ], snapped ["height" ]
390502
391503 if use_custom_calc and selected_category :
392- calculated = apply_custom_calculation (width , height , selected_category , presets )
504+ calculated = apply_custom_calculation (
505+ width ,
506+ height ,
507+ selected_category ,
508+ presets ,
509+ calculation_profile ,
510+ )
393511 width , height = calculated ["width" ], calculated ["height" ]
394512
395513 elif action in ("rescale" , "target_resolution_from_scale" ):
0 commit comments