From 40ee700719bc31b3ad48c8ecb28195398c6f9de0 Mon Sep 17 00:00:00 2001 From: luojiyin Date: Wed, 9 Sep 2026 11:44:59 +0800 Subject: [PATCH 1/4] fix: harden mipmap creation failure handling --- src/viv.c | 68 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/src/viv.c b/src/viv.c index deff4526..0ab49352 100644 --- a/src/viv.c +++ b/src/viv.c @@ -14201,17 +14201,22 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int { if (!*pmip) { + _viv_mipmap_t *mipmap; HDC screen_hdc; HDC mem_hdc; HDC mem2_hdc; HGDIOBJ last_hbitmap; HGDIOBJ last2_hbitmap; int last_stretch_mode; + int success; debug_printf("GETMIPMAP %d: %d %d\n",depth,mip_wide,mip_high); // create mipmap.. - *pmip = mem_alloc(sizeof(_viv_mipmap_t)); + mipmap = mem_alloc(sizeof(*mipmap)); + mipmap->hbitmap = NULL; + mipmap->mipmap = NULL; + success = 0; screen_hdc = GetDC(0); if (screen_hdc) @@ -14222,31 +14227,30 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int mem2_hdc = CreateCompatibleDC(screen_hdc); if (mem2_hdc) { - (*pmip)->mipmap = NULL; - (*pmip)->hbitmap = CreateCompatibleBitmap(screen_hdc,mip_wide,mip_high); - - last_hbitmap = SelectObject(mem_hdc,(*pmip)->hbitmap); - last2_hbitmap = SelectObject(mem2_hdc,best_hbitmap); - - last_stretch_mode = SetStretchBltMode(mem_hdc,HALFTONE); - - // for crazy large images -https://github.com/voidtools/voidImageViewer/issues/45 - // use stitching, since we are using HALFTONE here, we will end up with sharp tile edges - // but's its better than showing a black image. - if (_viv_StretchBltStitch(mem_hdc,0,0,mip_wide,mip_high,mem2_hdc,0,0,best_wide,best_high,SRCCOPY,0,0,mip_wide,mip_high)) - { - // OK - } - else + mipmap->hbitmap = CreateCompatibleBitmap(screen_hdc,mip_wide,mip_high); + if (mipmap->hbitmap) { - debug_printf("get_mipmap %d %d failed %u\n",mip_wide,mip_high,GetLastError()); + last_hbitmap = SelectObject(mem_hdc,mipmap->hbitmap); + if (last_hbitmap && (last_hbitmap != HGDI_ERROR)) + { + last2_hbitmap = SelectObject(mem2_hdc,best_hbitmap); + if (last2_hbitmap && (last2_hbitmap != HGDI_ERROR)) + { + last_stretch_mode = SetStretchBltMode(mem_hdc,HALFTONE); + if (last_stretch_mode) + { + // Stitch large images to stay within GDI limits. + success = _viv_StretchBltStitch(mem_hdc,0,0,mip_wide,mip_high,mem2_hdc,0,0,best_wide,best_high,SRCCOPY,0,0,mip_wide,mip_high); + SetStretchBltMode(mem_hdc,last_stretch_mode); + } + + SelectObject(mem2_hdc,last2_hbitmap); + } + + SelectObject(mem_hdc,last_hbitmap); + } } - SetStretchBltMode(mem_hdc,last_stretch_mode); - - SelectObject(mem_hdc,last_hbitmap); - SelectObject(mem2_hdc,last2_hbitmap); - DeleteDC(mem2_hdc); } @@ -14255,6 +14259,24 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int ReleaseDC(0,screen_hdc); } + + if (!success) + { + debug_printf("get_mipmap %d %d failed\n",mip_wide,mip_high); + + if (mipmap->hbitmap) + { + DeleteObject(mipmap->hbitmap); + } + + mem_free(mipmap); + *pmip_wide = best_wide; + *pmip_high = best_high; + return best_hbitmap; + } + + // Publish only complete mipmaps. + *pmip = mipmap; } best_wide = mip_wide; From c7d07093b008fea9622c2a839ae8e9b871992df3 Mon Sep 17 00:00:00 2001 From: luojiyin Date: Wed, 9 Sep 2026 11:55:19 +0800 Subject: [PATCH 2/4] fix: preserve mipmap creation error details --- src/viv.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/viv.c b/src/viv.c index 0ab49352..19bb235b 100644 --- a/src/viv.c +++ b/src/viv.c @@ -14209,6 +14209,7 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int HGDIOBJ last2_hbitmap; int last_stretch_mode; int success; + DWORD error; debug_printf("GETMIPMAP %d: %d %d\n",depth,mip_wide,mip_high); @@ -14217,6 +14218,7 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int mipmap->hbitmap = NULL; mipmap->mipmap = NULL; success = 0; + error = ERROR_SUCCESS; screen_hdc = GetDC(0); if (screen_hdc) @@ -14241,28 +14243,61 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int { // Stitch large images to stay within GDI limits. success = _viv_StretchBltStitch(mem_hdc,0,0,mip_wide,mip_high,mem2_hdc,0,0,best_wide,best_high,SRCCOPY,0,0,mip_wide,mip_high); + if (!success) + { + error = GetLastError(); + } + SetStretchBltMode(mem_hdc,last_stretch_mode); } + else + { + error = GetLastError(); + } SelectObject(mem2_hdc,last2_hbitmap); } + else + { + error = GetLastError(); + } SelectObject(mem_hdc,last_hbitmap); } + else + { + error = GetLastError(); + } + } + else + { + error = GetLastError(); } DeleteDC(mem2_hdc); } + else + { + error = GetLastError(); + } DeleteDC(mem_hdc); } + else + { + error = GetLastError(); + } ReleaseDC(0,screen_hdc); } + else + { + error = GetLastError(); + } if (!success) { - debug_printf("get_mipmap %d %d failed\n",mip_wide,mip_high); + debug_printf("get_mipmap %d %d failed %u\n",mip_wide,mip_high,error); if (mipmap->hbitmap) { From 29a0631d99bf6787f1650820e9709bbdd9f2aab6 Mon Sep 17 00:00:00 2001 From: luojiyin Date: Wed, 9 Sep 2026 12:00:18 +0800 Subject: [PATCH 3/4] refactor: simplify mipmap failure cleanup --- src/viv.c | 154 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 86 insertions(+), 68 deletions(-) diff --git a/src/viv.c b/src/viv.c index 19bb235b..1841ff32 100644 --- a/src/viv.c +++ b/src/viv.c @@ -14217,82 +14217,100 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int mipmap = mem_alloc(sizeof(*mipmap)); mipmap->hbitmap = NULL; mipmap->mipmap = NULL; + screen_hdc = NULL; + mem_hdc = NULL; + mem2_hdc = NULL; + last_hbitmap = NULL; + last2_hbitmap = NULL; + last_stretch_mode = 0; success = 0; error = ERROR_SUCCESS; screen_hdc = GetDC(0); - if (screen_hdc) + if (!screen_hdc) { - mem_hdc = CreateCompatibleDC(screen_hdc); - if (mem_hdc) - { - mem2_hdc = CreateCompatibleDC(screen_hdc); - if (mem2_hdc) - { - mipmap->hbitmap = CreateCompatibleBitmap(screen_hdc,mip_wide,mip_high); - if (mipmap->hbitmap) - { - last_hbitmap = SelectObject(mem_hdc,mipmap->hbitmap); - if (last_hbitmap && (last_hbitmap != HGDI_ERROR)) - { - last2_hbitmap = SelectObject(mem2_hdc,best_hbitmap); - if (last2_hbitmap && (last2_hbitmap != HGDI_ERROR)) - { - last_stretch_mode = SetStretchBltMode(mem_hdc,HALFTONE); - if (last_stretch_mode) - { - // Stitch large images to stay within GDI limits. - success = _viv_StretchBltStitch(mem_hdc,0,0,mip_wide,mip_high,mem2_hdc,0,0,best_wide,best_high,SRCCOPY,0,0,mip_wide,mip_high); - if (!success) - { - error = GetLastError(); - } - - SetStretchBltMode(mem_hdc,last_stretch_mode); - } - else - { - error = GetLastError(); - } - - SelectObject(mem2_hdc,last2_hbitmap); - } - else - { - error = GetLastError(); - } - - SelectObject(mem_hdc,last_hbitmap); - } - else - { - error = GetLastError(); - } - } - else - { - error = GetLastError(); - } - - DeleteDC(mem2_hdc); - } - else - { - error = GetLastError(); - } - - DeleteDC(mem_hdc); - } - else - { - error = GetLastError(); - } - - ReleaseDC(0,screen_hdc); + error = GetLastError(); + goto mipmap_cleanup; } - else + + mem_hdc = CreateCompatibleDC(screen_hdc); + if (!mem_hdc) { error = GetLastError(); + goto mipmap_cleanup; + } + + mem2_hdc = CreateCompatibleDC(screen_hdc); + if (!mem2_hdc) + { + error = GetLastError(); + goto mipmap_cleanup; + } + + mipmap->hbitmap = CreateCompatibleBitmap(screen_hdc,mip_wide,mip_high); + if (!mipmap->hbitmap) + { + error = GetLastError(); + goto mipmap_cleanup; + } + + last_hbitmap = SelectObject(mem_hdc,mipmap->hbitmap); + if ((!last_hbitmap) || (last_hbitmap == HGDI_ERROR)) + { + error = GetLastError(); + goto mipmap_cleanup; + } + + last2_hbitmap = SelectObject(mem2_hdc,best_hbitmap); + if ((!last2_hbitmap) || (last2_hbitmap == HGDI_ERROR)) + { + error = GetLastError(); + goto mipmap_cleanup; + } + + last_stretch_mode = SetStretchBltMode(mem_hdc,HALFTONE); + if (!last_stretch_mode) + { + error = GetLastError(); + goto mipmap_cleanup; + } + + // Stitch large images to stay within GDI limits. + success = _viv_StretchBltStitch(mem_hdc,0,0,mip_wide,mip_high,mem2_hdc,0,0,best_wide,best_high,SRCCOPY,0,0,mip_wide,mip_high); + if (!success) + { + error = GetLastError(); + } + + mipmap_cleanup: + if (last_stretch_mode) + { + SetStretchBltMode(mem_hdc,last_stretch_mode); + } + + if (last2_hbitmap && (last2_hbitmap != HGDI_ERROR)) + { + SelectObject(mem2_hdc,last2_hbitmap); + } + + if (last_hbitmap && (last_hbitmap != HGDI_ERROR)) + { + SelectObject(mem_hdc,last_hbitmap); + } + + if (mem2_hdc) + { + DeleteDC(mem2_hdc); + } + + if (mem_hdc) + { + DeleteDC(mem_hdc); + } + + if (screen_hdc) + { + ReleaseDC(0,screen_hdc); } if (!success) From d1a678e525aa28ed3203e3a67b196925b81a0f55 Mon Sep 17 00:00:00 2001 From: luojiyin Date: Wed, 9 Sep 2026 12:09:09 +0800 Subject: [PATCH 4/4] refactor: isolate mipmap bitmap creation --- src/viv.c | 230 +++++++++++++++++++++++++++++------------------------- 1 file changed, 122 insertions(+), 108 deletions(-) diff --git a/src/viv.c b/src/viv.c index 1841ff32..8e98ced7 100644 --- a/src/viv.c +++ b/src/viv.c @@ -14143,6 +14143,127 @@ int viv_get_command_count(void) return _VIV_COMMAND_COUNT; } +static HBITMAP _viv_create_mipmap_bitmap(HBITMAP source_hbitmap,int source_wide,int source_high,int mip_wide,int mip_high,DWORD *out_error) +{ + HDC screen_hdc; + HDC mem_hdc; + HDC mem2_hdc; + HBITMAP hbitmap; + HGDIOBJ last_hbitmap; + HGDIOBJ last2_hbitmap; + int last_stretch_mode; + int success; + + screen_hdc = NULL; + mem_hdc = NULL; + mem2_hdc = NULL; + hbitmap = NULL; + last_hbitmap = NULL; + last2_hbitmap = NULL; + last_stretch_mode = 0; + success = 0; + *out_error = ERROR_SUCCESS; + + screen_hdc = GetDC(0); + if (!screen_hdc) + { + *out_error = GetLastError(); + goto cleanup; + } + + mem_hdc = CreateCompatibleDC(screen_hdc); + if (!mem_hdc) + { + *out_error = GetLastError(); + goto cleanup; + } + + mem2_hdc = CreateCompatibleDC(screen_hdc); + if (!mem2_hdc) + { + *out_error = GetLastError(); + goto cleanup; + } + + hbitmap = CreateCompatibleBitmap(screen_hdc,mip_wide,mip_high); + if (!hbitmap) + { + *out_error = GetLastError(); + goto cleanup; + } + + last_hbitmap = SelectObject(mem_hdc,hbitmap); + if ((!last_hbitmap) || (last_hbitmap == HGDI_ERROR)) + { + *out_error = GetLastError(); + goto cleanup; + } + + last2_hbitmap = SelectObject(mem2_hdc,source_hbitmap); + if ((!last2_hbitmap) || (last2_hbitmap == HGDI_ERROR)) + { + *out_error = GetLastError(); + goto cleanup; + } + + last_stretch_mode = SetStretchBltMode(mem_hdc,HALFTONE); + if (!last_stretch_mode) + { + *out_error = GetLastError(); + goto cleanup; + } + + // Stitch large images to stay within GDI limits. + success = _viv_StretchBltStitch(mem_hdc,0,0,mip_wide,mip_high,mem2_hdc,0,0,source_wide,source_high,SRCCOPY,0,0,mip_wide,mip_high); + if (!success) + { + *out_error = GetLastError(); + } + +cleanup: + if (last_stretch_mode) + { + SetStretchBltMode(mem_hdc,last_stretch_mode); + } + + if (last2_hbitmap && (last2_hbitmap != HGDI_ERROR)) + { + SelectObject(mem2_hdc,last2_hbitmap); + } + + if (last_hbitmap && (last_hbitmap != HGDI_ERROR)) + { + SelectObject(mem_hdc,last_hbitmap); + } + + if (mem2_hdc) + { + DeleteDC(mem2_hdc); + } + + if (mem_hdc) + { + DeleteDC(mem_hdc); + } + + if (screen_hdc) + { + ReleaseDC(0,screen_hdc); + } + + if (!success) + { + if (hbitmap) + { + DeleteObject(hbitmap); + } + + return NULL; + } + + return hbitmap; +} + static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int render_wide,int render_high,int *pmip_wide,int *pmip_high,_viv_mipmap_t **out_mip) { int mip_wide; @@ -14202,126 +14323,19 @@ static HBITMAP _viv_get_mipmap(HBITMAP hbitmap,int image_wide,int image_high,int if (!*pmip) { _viv_mipmap_t *mipmap; - HDC screen_hdc; - HDC mem_hdc; - HDC mem2_hdc; - HGDIOBJ last_hbitmap; - HGDIOBJ last2_hbitmap; - int last_stretch_mode; - int success; DWORD error; debug_printf("GETMIPMAP %d: %d %d\n",depth,mip_wide,mip_high); // create mipmap.. mipmap = mem_alloc(sizeof(*mipmap)); - mipmap->hbitmap = NULL; mipmap->mipmap = NULL; - screen_hdc = NULL; - mem_hdc = NULL; - mem2_hdc = NULL; - last_hbitmap = NULL; - last2_hbitmap = NULL; - last_stretch_mode = 0; - success = 0; - error = ERROR_SUCCESS; - - screen_hdc = GetDC(0); - if (!screen_hdc) - { - error = GetLastError(); - goto mipmap_cleanup; - } - - mem_hdc = CreateCompatibleDC(screen_hdc); - if (!mem_hdc) - { - error = GetLastError(); - goto mipmap_cleanup; - } - - mem2_hdc = CreateCompatibleDC(screen_hdc); - if (!mem2_hdc) - { - error = GetLastError(); - goto mipmap_cleanup; - } + mipmap->hbitmap = _viv_create_mipmap_bitmap(best_hbitmap,best_wide,best_high,mip_wide,mip_high,&error); - mipmap->hbitmap = CreateCompatibleBitmap(screen_hdc,mip_wide,mip_high); if (!mipmap->hbitmap) - { - error = GetLastError(); - goto mipmap_cleanup; - } - - last_hbitmap = SelectObject(mem_hdc,mipmap->hbitmap); - if ((!last_hbitmap) || (last_hbitmap == HGDI_ERROR)) - { - error = GetLastError(); - goto mipmap_cleanup; - } - - last2_hbitmap = SelectObject(mem2_hdc,best_hbitmap); - if ((!last2_hbitmap) || (last2_hbitmap == HGDI_ERROR)) - { - error = GetLastError(); - goto mipmap_cleanup; - } - - last_stretch_mode = SetStretchBltMode(mem_hdc,HALFTONE); - if (!last_stretch_mode) - { - error = GetLastError(); - goto mipmap_cleanup; - } - - // Stitch large images to stay within GDI limits. - success = _viv_StretchBltStitch(mem_hdc,0,0,mip_wide,mip_high,mem2_hdc,0,0,best_wide,best_high,SRCCOPY,0,0,mip_wide,mip_high); - if (!success) - { - error = GetLastError(); - } - - mipmap_cleanup: - if (last_stretch_mode) - { - SetStretchBltMode(mem_hdc,last_stretch_mode); - } - - if (last2_hbitmap && (last2_hbitmap != HGDI_ERROR)) - { - SelectObject(mem2_hdc,last2_hbitmap); - } - - if (last_hbitmap && (last_hbitmap != HGDI_ERROR)) - { - SelectObject(mem_hdc,last_hbitmap); - } - - if (mem2_hdc) - { - DeleteDC(mem2_hdc); - } - - if (mem_hdc) - { - DeleteDC(mem_hdc); - } - - if (screen_hdc) - { - ReleaseDC(0,screen_hdc); - } - - if (!success) { debug_printf("get_mipmap %d %d failed %u\n",mip_wide,mip_high,error); - if (mipmap->hbitmap) - { - DeleteObject(mipmap->hbitmap); - } - mem_free(mipmap); *pmip_wide = best_wide; *pmip_high = best_high;