1313from dataclasses import dataclass
1414from typing import Dict , Iterable , List , Tuple
1515
16- MAX_COMMENT_BASE64 = 40_000
16+ MAX_COMMENT_BASE64 = 60_000
1717
1818PNG_SIGNATURE = b"\x89 PNG\r \n \x1a \n "
1919
@@ -221,44 +221,19 @@ def chunk(tag: bytes, payload: bytes) -> bytes:
221221 )
222222
223223
224- def _downscale_half (width : int , height : int , bpp : int , pixels : bytes ) -> Tuple [int , int , bytes ]:
225- new_width = max (1 , (width + 1 ) // 2 )
226- new_height = max (1 , (height + 1 ) // 2 )
227- new_pixels = bytearray (new_width * new_height * bpp )
228-
229- for ny in range (new_height ):
230- for nx in range (new_width ):
231- accum = [0 ] * bpp
232- samples = 0
233- for dy in (0 , 1 ):
234- sy = min (height - 1 , ny * 2 + dy )
235- for dx in (0 , 1 ):
236- sx = min (width - 1 , nx * 2 + dx )
237- src_index = (sy * width + sx ) * bpp
238- for channel in range (bpp ):
239- accum [channel ] += pixels [src_index + channel ]
240- samples += 1
241- dst_index = (ny * new_width + nx ) * bpp
242- for channel in range (bpp ):
243- new_pixels [dst_index + channel ] = accum [channel ] // samples
244-
245- return new_width , new_height , bytes (new_pixels )
246-
247-
248- def build_preview_base64 (image : PNGImage , max_length : int = MAX_COMMENT_BASE64 ) -> str :
249- width = image .width
250- height = image .height
251- bpp = image .bytes_per_pixel
252- pixels = image .pixels
253-
254- while True :
255- png_bytes = _encode_png (width , height , image .bit_depth , image .color_type , bpp , pixels )
256- encoded = base64 .b64encode (png_bytes ).decode ("ascii" )
257- if len (encoded ) <= max_length or width <= 1 or height <= 1 :
258- return encoded
259- if image .color_type not in {0 , 2 , 4 , 6 }:
260- return encoded
261- width , height , pixels = _downscale_half (width , height , bpp , pixels )
224+ def build_base64_payload (image : PNGImage , max_length : int = MAX_COMMENT_BASE64 ) -> Tuple [str | None , int ]:
225+ png_bytes = _encode_png (
226+ image .width ,
227+ image .height ,
228+ image .bit_depth ,
229+ image .color_type ,
230+ image .bytes_per_pixel ,
231+ image .pixels ,
232+ )
233+ encoded = base64 .b64encode (png_bytes ).decode ("ascii" )
234+ if len (encoded ) <= max_length :
235+ return encoded , len (encoded )
236+ return None , len (encoded )
262237
263238
264239def build_results (reference_dir : pathlib .Path , actual_entries : List [Tuple [str , pathlib .Path ]], emit_base64 : bool ) -> Dict [str , List [Dict [str , object ]]]:
@@ -275,10 +250,19 @@ def build_results(reference_dir: pathlib.Path, actual_entries: List[Tuple[str, p
275250 elif not expected_path .exists ():
276251 record .update ({"status" : "missing_expected" })
277252 if emit_base64 :
253+ payload = None
254+ length = 0
278255 try :
279- record [ "base64" ] = build_preview_base64 (load_png (actual_path ))
256+ payload , length = build_base64_payload (load_png (actual_path ))
280257 except Exception :
281- record ["base64" ] = base64 .b64encode (actual_path .read_bytes ()).decode ("ascii" )
258+ raw = base64 .b64encode (actual_path .read_bytes ()).decode ("ascii" )
259+ length = len (raw )
260+ if length <= MAX_COMMENT_BASE64 :
261+ payload = raw
262+ if payload is not None :
263+ record ["base64" ] = payload
264+ elif length :
265+ record .update ({"base64_omitted" : "too_large" , "base64_length" : length })
282266 else :
283267 try :
284268 actual_img = load_png (actual_path )
@@ -292,10 +276,19 @@ def build_results(reference_dir: pathlib.Path, actual_entries: List[Tuple[str, p
292276 else :
293277 record .update ({"status" : "different" , "details" : outcome })
294278 if emit_base64 :
279+ payload = None
280+ length = 0
295281 try :
296- record [ "base64" ] = build_preview_base64 (actual_img )
282+ payload , length = build_base64_payload (actual_img )
297283 except Exception :
298- record ["base64" ] = base64 .b64encode (actual_path .read_bytes ()).decode ("ascii" )
284+ raw = base64 .b64encode (actual_path .read_bytes ()).decode ("ascii" )
285+ length = len (raw )
286+ if length <= MAX_COMMENT_BASE64 :
287+ payload = raw
288+ if payload is not None :
289+ record ["base64" ] = payload
290+ elif length :
291+ record .update ({"base64_omitted" : "too_large" , "base64_length" : length })
299292 results .append (record )
300293 return {"results" : results }
301294
0 commit comments