A fork of https://github.com/crosspoint-reader/crosspoint-reader
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: jpeg resource cleanup (#1320)

## Summary

* **What is the goal of this PR?** Fix leak on decode error path in JPEG
converter
* **What changes are included?**
Unif resource cleanup

## Additional Context

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**PARTIALLY**_
Identification of the issue by AI

authored by

jpirnay and committed by
GitHub
b5df6cb2 16b73744

+33 -33
+33 -33
lib/JpegToBmpConverter/JpegToBmpConverter.cpp
··· 278 278 bytesPerRow = (outWidth * 2 + 31) / 32 * 4; 279 279 } 280 280 281 + uint8_t* rowBuffer = nullptr; 282 + uint8_t* mcuRowBuffer = nullptr; 283 + AtkinsonDitherer* atkinsonDitherer = nullptr; 284 + FloydSteinbergDitherer* fsDitherer = nullptr; 285 + Atkinson1BitDitherer* atkinson1BitDitherer = nullptr; 286 + uint32_t* rowAccum = nullptr; // Accumulator for each output X (32-bit for larger sums) 287 + uint32_t* rowCount = nullptr; // Count of source pixels accumulated per output X 288 + 289 + // RAII guard: frees all heap resources on any return path, including early exits. 290 + // Holds references so it always sees the latest pointer values assigned below. 291 + struct Cleanup { 292 + uint8_t*& rowBuffer; 293 + uint8_t*& mcuRowBuffer; 294 + AtkinsonDitherer*& atkinsonDitherer; 295 + FloydSteinbergDitherer*& fsDitherer; 296 + Atkinson1BitDitherer*& atkinson1BitDitherer; 297 + uint32_t*& rowAccum; 298 + uint32_t*& rowCount; 299 + ~Cleanup() { 300 + delete[] rowAccum; 301 + delete[] rowCount; 302 + delete atkinsonDitherer; 303 + delete fsDitherer; 304 + delete atkinson1BitDitherer; 305 + free(mcuRowBuffer); 306 + free(rowBuffer); 307 + } 308 + } cleanup{rowBuffer, mcuRowBuffer, atkinsonDitherer, fsDitherer, atkinson1BitDitherer, rowAccum, rowCount}; 309 + 281 310 // Allocate row buffer 282 - auto* rowBuffer = static_cast<uint8_t*>(malloc(bytesPerRow)); 311 + rowBuffer = static_cast<uint8_t*>(malloc(bytesPerRow)); 283 312 if (!rowBuffer) { 284 313 LOG_ERR("JPG", "Failed to allocate row buffer"); 285 314 return false; ··· 293 322 // Validate MCU row buffer size before allocation 294 323 if (mcuRowPixels > MAX_MCU_ROW_BYTES) { 295 324 LOG_DBG("JPG", "MCU row buffer too large (%d bytes), max: %d", mcuRowPixels, MAX_MCU_ROW_BYTES); 296 - free(rowBuffer); 297 325 return false; 298 326 } 299 327 300 - auto* mcuRowBuffer = static_cast<uint8_t*>(malloc(mcuRowPixels)); 328 + mcuRowBuffer = static_cast<uint8_t*>(malloc(mcuRowPixels)); 301 329 if (!mcuRowBuffer) { 302 330 LOG_ERR("JPG", "Failed to allocate MCU row buffer (%d bytes)", mcuRowPixels); 303 - free(rowBuffer); 304 331 return false; 305 332 } 306 333 307 334 // Create ditherer if enabled 308 335 // Use OUTPUT dimensions for dithering (after prescaling) 309 - AtkinsonDitherer* atkinsonDitherer = nullptr; 310 - FloydSteinbergDitherer* fsDitherer = nullptr; 311 - Atkinson1BitDitherer* atkinson1BitDitherer = nullptr; 312 - 313 336 if (oneBit) { 314 337 // For 1-bit output, use Atkinson dithering for better quality 315 338 atkinson1BitDitherer = new Atkinson1BitDitherer(outWidth); ··· 324 347 // For scaling: accumulate source rows into scaled output rows 325 348 // We need to track which source Y maps to which output Y 326 349 // Using fixed-point: srcY_fp = outY * scaleY_fp (gives source Y in 16.16 format) 327 - uint32_t* rowAccum = nullptr; // Accumulator for each output X (32-bit for larger sums) 328 - uint16_t* rowCount = nullptr; // Count of source pixels accumulated per output X 329 350 int currentOutY = 0; // Current output row being accumulated 330 351 uint32_t nextOutY_srcStart = 0; // Source Y where next output row starts (16.16 fixed point) 331 352 332 353 if (needsScaling) { 333 354 rowAccum = new uint32_t[outWidth](); 334 - rowCount = new uint16_t[outWidth](); 355 + rowCount = new uint32_t[outWidth](); 335 356 nextOutY_srcStart = scaleY_fp; // First boundary is at scaleY_fp (source Y for outY=1) 336 357 } 337 358 ··· 351 372 } else { 352 373 LOG_ERR("JPG", "JPEG decode MCU failed at (%d, %d) with error code: %d", mcuX, mcuY, mcuStatus); 353 374 } 354 - free(mcuRowBuffer); 355 - free(rowBuffer); 356 375 return false; 357 376 } 358 377 ··· 528 547 } 529 548 // Moving to next source row - reset accumulators 530 549 memset(rowAccum, 0, outWidth * sizeof(uint32_t)); 531 - memset(rowCount, 0, outWidth * sizeof(uint16_t)); 550 + memset(rowCount, 0, outWidth * sizeof(uint32_t)); 532 551 } 533 552 } 534 553 } 535 554 } 536 - 537 - // Clean up 538 - if (rowAccum) { 539 - delete[] rowAccum; 540 - } 541 - if (rowCount) { 542 - delete[] rowCount; 543 - } 544 - if (atkinsonDitherer) { 545 - delete atkinsonDitherer; 546 - } 547 - if (fsDitherer) { 548 - delete fsDitherer; 549 - } 550 - if (atkinson1BitDitherer) { 551 - delete atkinson1BitDitherer; 552 - } 553 - free(mcuRowBuffer); 554 - free(rowBuffer); 555 555 556 556 LOG_DBG("JPG", "Successfully converted JPEG to BMP"); 557 557 return true;