1 /*
   2  * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 // copy from awt.h
  27 #ifndef _WIN32_WINNT
  28 #define _WIN32_WINNT 0x0600
  29 #endif
  30 
  31 // copy from awt.h
  32 #ifndef _WIN32_IE
  33 #define _WIN32_IE 0x0600
  34 #endif
  35 
  36 #include "splashscreen_impl.h"
  37 #include <windowsx.h>
  38 #include <windows.h>
  39 #include <winuser.h>
  40 #include "sizecalc.h"
  41 #ifndef WS_EX_LAYERED
  42 #define WS_EX_LAYERED 0x80000
  43 #endif
  44 
  45 #ifndef ULW_ALPHA
  46 #define ULW_ALPHA               0x00000002
  47 #endif
  48 
  49 #ifndef AC_SRC_OVER
  50 #define AC_SRC_OVER                 0x00
  51 #endif
  52 
  53 #ifndef AC_SRC_ALPHA
  54 #define AC_SRC_ALPHA                0x01
  55 #endif
  56 
  57 #define WM_SPLASHUPDATE         WM_USER+1
  58 #define WM_SPLASHRECONFIGURE    WM_USER+2
  59 
  60 #define BUFF_SIZE 1024
  61 
  62 /* Could use npt but decided to cut down on linked code size */
  63 char* SplashConvertStringAlloc(const char* in, int *size) {
  64     int len, outChars, rc;
  65     WCHAR* buf;
  66     if (!in) {
  67         return NULL;
  68     }
  69     len = strlen(in);
  70     outChars = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, in, len,
  71                                        NULL, 0);
  72     buf = (WCHAR*) SAFE_SIZE_ARRAY_ALLOC(malloc, outChars, sizeof(WCHAR));
  73     if (!buf) {
  74         return NULL;
  75     }
  76     rc = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, in, len,
  77                                  buf, outChars);
  78     if (rc==0) {
  79         free(buf);
  80         return NULL;
  81     } else {
  82         if (size) {
  83             *size = rc;
  84         }
  85         return (char*)buf;
  86     }
  87 }
  88 
  89 unsigned
  90 SplashTime(void)
  91 {
  92     return GetTickCount();
  93 }
  94 
  95 void
  96 SplashInitFrameShape(Splash * splash, int imageIndex)
  97 {
  98     RGNDATA *pRgnData;
  99     RGNDATAHEADER *pRgnHdr;
 100     ImageRect maskRect;
 101 
 102     if (!splash->maskRequired)
 103         return;
 104 
 105     /* reserving memory for the worst case */
 106     if (!IS_SAFE_SIZE_MUL(splash->width / 2 + 1, splash->height)) {
 107         return;
 108     }
 109     pRgnData = (RGNDATA *) SAFE_SIZE_STRUCT_ALLOC(malloc, sizeof(RGNDATAHEADER),
 110             sizeof(RECT), (splash->width / 2 + 1) * splash->height);
 111     if (!pRgnData) {
 112         return;
 113     }
 114     pRgnHdr = (RGNDATAHEADER *) pRgnData;
 115     initRect(&maskRect, 0, 0, splash->width, splash->height, 1,
 116             splash->width * splash->imageFormat.depthBytes,
 117             splash->frames[imageIndex].bitmapBits, &splash->imageFormat);
 118 
 119     pRgnHdr->dwSize = sizeof(RGNDATAHEADER);
 120     pRgnHdr->iType = RDH_RECTANGLES;
 121     pRgnHdr->nRgnSize = 0;
 122     pRgnHdr->rcBound.top = 0;
 123     pRgnHdr->rcBound.left = 0;
 124     pRgnHdr->rcBound.bottom = splash->height;
 125     pRgnHdr->rcBound.right = splash->width;
 126 
 127     pRgnHdr->nCount = BitmapToYXBandedRectangles(&maskRect,
 128             (RECT *) (((BYTE *) pRgnData) + sizeof(RGNDATAHEADER)));
 129 
 130     splash->frames[imageIndex].hRgn = ExtCreateRegion(NULL,
 131             sizeof(RGNDATAHEADER) + sizeof(RECT) * pRgnHdr->nCount, pRgnData);
 132 
 133     free(pRgnData);
 134 }
 135 
 136 /* paint current splash screen frame to hdc
 137    this function is unused in layered window mode */
 138 
 139 void
 140 SplashPaint(Splash * splash, HDC hdc)
 141 {
 142     unsigned numColors = splash->screenFormat.colorMap ?
 143         splash->screenFormat.numColors : 0;
 144     BITMAPV4HEADER *pBmi;
 145     HPALETTE hOldPal = NULL;
 146 
 147     if (!splash->frames)
 148         return;
 149     if (splash->currentFrame < 0 || splash->currentFrame >= splash->frameCount)
 150         return;
 151     pBmi = (BITMAPV4HEADER *) SAFE_SIZE_STRUCT_ALLOC(alloca, sizeof(BITMAPV4HEADER),
 152             sizeof(RGBQUAD), numColors);
 153     if (!pBmi) {
 154         return;
 155     }
 156     memset(pBmi, 0, sizeof(BITMAPV4HEADER));
 157     if (splash->screenFormat.colorMap)
 158         memcpy(((BYTE *) pBmi) + sizeof(BITMAPV4HEADER),
 159                 splash->screenFormat.colorMap, sizeof(RGBQUAD) * numColors);
 160 
 161     pBmi->bV4Size = sizeof(BITMAPV4HEADER);
 162     pBmi->bV4Width = splash->width;
 163     pBmi->bV4Height = -splash->height;
 164     pBmi->bV4Planes = 1;
 165     pBmi->bV4BitCount = (WORD) (splash->screenFormat.depthBytes * 8);
 166     /* we're ALWAYS using BGRA in screenFormat */
 167     pBmi->bV4V4Compression = BI_RGB;
 168     pBmi->bV4ClrUsed = numColors;
 169     pBmi->bV4ClrImportant = numColors;
 170     pBmi->bV4AlphaMask = splash->screenFormat.mask[3];
 171     pBmi->bV4RedMask = splash->screenFormat.mask[2];
 172     pBmi->bV4GreenMask = splash->screenFormat.mask[1];
 173     pBmi->bV4BlueMask = splash->screenFormat.mask[0];
 174 
 175     /*  creating the palette in SplashInitPlatform does not work, so I'm creating it
 176        here on demand */
 177     if (!splash->hPalette) {
 178         unsigned i;
 179         LOGPALETTE *pLogPal = (LOGPALETTE *) SAFE_SIZE_STRUCT_ALLOC(malloc,
 180                 sizeof(LOGPALETTE), sizeof(PALETTEENTRY), numColors);
 181         if (!pLogPal) {
 182             return;
 183         }
 184 
 185         pLogPal->palVersion = 0x300;
 186         pLogPal->palNumEntries = (WORD) numColors;
 187         for (i = 0; i < numColors; i++) {
 188             pLogPal->palPalEntry[i].peRed = (BYTE)
 189                 QUAD_RED(splash->colorMap[i]);
 190             pLogPal->palPalEntry[i].peGreen = (BYTE)
 191                 QUAD_GREEN(splash->colorMap[i]);
 192             pLogPal->palPalEntry[i].peBlue = (BYTE)
 193                 QUAD_BLUE(splash->colorMap[i]);
 194             pLogPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
 195         }
 196         splash->hPalette = CreatePalette(pLogPal);
 197         free(pLogPal);
 198     }
 199     if (splash->hPalette) {
 200         hOldPal = SelectPalette(hdc, splash->hPalette, FALSE);
 201         RealizePalette(hdc);
 202     }
 203 
 204     StretchDIBits(hdc, 0, 0, splash->width, splash->height, 0, 0,
 205             splash->width, splash->height, splash->screenData,
 206             (BITMAPINFO *) pBmi, DIB_RGB_COLORS, SRCCOPY);
 207     if (hOldPal)
 208         SelectPalette(hdc, hOldPal, FALSE);
 209 }
 210 
 211 
 212 /* The function makes the window visible if it is hidden
 213  or is not yet shown. */
 214 void
 215 SplashRedrawWindow(Splash * splash)
 216 {
 217     if (!SplashIsStillLooping(splash)) {
 218         KillTimer(splash->hWnd, 0);
 219     }
 220 
 221     if (splash->currentFrame < 0) {
 222         return;
 223     }
 224 
 225     SplashUpdateScreenData(splash);
 226     if (splash->isLayered) {
 227         BLENDFUNCTION bf;
 228         POINT ptSrc;
 229         HDC hdcSrc = CreateCompatibleDC(NULL), hdcDst;
 230         BITMAPINFOHEADER bmi;
 231         void *bitmapBits;
 232         HBITMAP hBitmap, hOldBitmap;
 233         RECT rect;
 234         POINT ptDst;
 235         SIZE size;
 236 
 237         bf.BlendOp = AC_SRC_OVER;
 238         bf.BlendFlags = 0;
 239         bf.AlphaFormat = AC_SRC_ALPHA;
 240         bf.SourceConstantAlpha = 0xFF;
 241         ptSrc.x = ptSrc.y = 0;
 242 
 243         memset(&bmi, 0, sizeof(bmi));
 244         bmi.biSize = sizeof(BITMAPINFOHEADER);
 245         bmi.biWidth = splash->width;
 246         bmi.biHeight = -splash->height;
 247         bmi.biPlanes = 1;
 248         bmi.biBitCount = 32;
 249         bmi.biCompression = BI_RGB;
 250 
 251         //      FIXME: this is somewhat ineffective
 252         //      maybe if we allocate memory for all frames as DIBSections,
 253         //      then we could select the frames into the DC directly
 254 
 255         hBitmap = CreateDIBSection(NULL, (BITMAPINFO *) & bmi, DIB_RGB_COLORS,
 256                 &bitmapBits, NULL, 0);
 257         memcpy(bitmapBits, splash->screenData,
 258                 splash->screenStride * splash->height);
 259         hOldBitmap = (HBITMAP) SelectObject(hdcSrc, hBitmap);
 260         hdcDst = GetDC(splash->hWnd);
 261 
 262         GetWindowRect(splash->hWnd, &rect);
 263 
 264         ptDst.x = rect.left;
 265         ptDst.y = rect.top;
 266 
 267         size.cx = splash->width;
 268         size.cy = splash->height;
 269 
 270         UpdateLayeredWindow(splash->hWnd, hdcDst, &ptDst, &size,
 271                 hdcSrc, &ptSrc, 0, &bf, ULW_ALPHA);
 272 
 273         ReleaseDC(splash->hWnd, hdcDst);
 274         SelectObject(hdcSrc, hOldBitmap);
 275         DeleteObject(hBitmap);
 276         DeleteDC(hdcSrc);
 277     }
 278     else {
 279        InvalidateRect(splash->hWnd, NULL, FALSE);
 280        if (splash->maskRequired) {
 281             HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
 282 
 283             CombineRgn(hRgn, splash->frames[splash->currentFrame].hRgn,
 284                     splash->frames[splash->currentFrame].hRgn, RGN_COPY);
 285             SetWindowRgn(splash->hWnd, hRgn, TRUE);
 286         } else {
 287             SetWindowRgn(splash->hWnd, NULL, TRUE);
 288         }
 289         UpdateWindow(splash->hWnd);
 290     }
 291     if (!IsWindowVisible(splash->hWnd)) {
 292         POINT cursorPos;
 293         ShowWindow(splash->hWnd, SW_SHOW);
 294         // Windows won't update the cursor after the window is shown,
 295         // if the cursor is already above the window. need to do this manually.
 296         GetCursorPos(&cursorPos);
 297         if (WindowFromPoint(cursorPos) == splash->hWnd) {
 298             // unfortunately Windows fail to understand that the window
 299             // thread should own the cursor, even though the mouse pointer
 300             // is over the window, until the mouse has been moved.
 301             // we're using SetCursorPos here to fake the mouse movement
 302             // and enable proper update of the cursor.
 303             SetCursorPos(cursorPos.x, cursorPos.y);
 304             SetCursor(LoadCursor(NULL, IDC_WAIT));
 305         }
 306     }
 307     if (SplashIsStillLooping(splash)) {
 308         int time = splash->time +
 309             splash->frames[splash->currentFrame].delay - SplashTime();
 310 
 311         if (time < 0)
 312             time = 0;
 313         SetTimer(splash->hWnd, 0, time, NULL);
 314     }
 315 }
 316 
 317 void SplashReconfigureNow(Splash * splash) {
 318     splash->x = (GetSystemMetrics(SM_CXSCREEN) - splash->width) / 2;
 319     splash->y = (GetSystemMetrics(SM_CYSCREEN) - splash->height) / 2;
 320     if (splash->hWnd) {
 321         //Fixed 6474657: splash screen image jumps towards left while
 322         //    setting the new image using setImageURL()
 323         // We may safely hide the splash window because SplashRedrawWindow()
 324         //    will show the window again.
 325         ShowWindow(splash->hWnd, SW_HIDE);
 326         MoveWindow(splash->hWnd, splash->x, splash->y, splash->width, splash->height, FALSE);
 327     }
 328     SplashRedrawWindow(splash);
 329 }
 330 
 331 static LRESULT CALLBACK
 332 SplashWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 333 {
 334     PAINTSTRUCT ps;
 335     HDC hdc;
 336 
 337 
 338     switch (message) {
 339 
 340     case WM_ERASEBKGND:
 341         return TRUE;            // to avoid flicker
 342 
 343     case WM_SYSCOMMAND:
 344         if (wParam==SC_CLOSE||wParam==SC_DEFAULT||wParam==SC_HOTKEY||
 345             wParam==SC_KEYMENU||wParam==SC_MAXIMIZE||
 346             wParam==SC_MINIMIZE||wParam==SC_MOUSEMENU||wParam==SC_MOVE||
 347             wParam==SC_RESTORE||wParam==SC_SIZE)
 348         {
 349             return 0;
 350         }
 351 
 352     /* double switch to avoid prologue/epilogue duplication */
 353     case WM_TIMER:
 354     case WM_SPLASHUPDATE:
 355     case WM_PAINT:
 356     case WM_SPLASHRECONFIGURE:
 357         {
 358             Splash *splash = (Splash *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
 359 
 360             SplashLock(splash);
 361             if (splash->isVisible>0) {
 362                 switch(message) {
 363                 case WM_TIMER:
 364                     SplashNextFrame(splash);
 365                     SplashRedrawWindow(splash);
 366                     break;
 367                 case WM_SPLASHUPDATE:
 368                     SplashRedrawWindow(splash);
 369                     break;
 370                 case WM_PAINT:
 371                     hdc = BeginPaint(hWnd, &ps);
 372                     SplashPaint(splash, hdc);
 373                     EndPaint(hWnd, &ps);
 374                     break;
 375                 case WM_SPLASHRECONFIGURE:
 376                     SplashReconfigureNow(splash);
 377                     break;
 378                 }
 379             }
 380             SplashUnlock(splash);
 381             break;
 382         }
 383     case WM_DESTROY:
 384         PostQuitMessage(0);
 385         break;
 386     default:
 387         return DefWindowProc(hWnd, message, wParam, lParam);
 388 
 389     }
 390     return 0;
 391 }
 392 
 393 HWND
 394 SplashCreateWindow(Splash * splash)
 395 {
 396     WNDCLASSEX wcex;
 397     ATOM wndClass;
 398     DWORD style, exStyle;
 399     HWND hWnd;
 400 
 401     ZeroMemory(&wcex, sizeof(WNDCLASSEX));
 402 
 403     wcex.cbSize = sizeof(WNDCLASSEX);
 404     wcex.style = CS_HREDRAW | CS_VREDRAW;
 405     wcex.lpfnWndProc = (WNDPROC) SplashWndProc;
 406     wcex.hInstance = GetModuleHandle(NULL);
 407     wcex.lpszClassName = "JavaSplash";
 408     wcex.hCursor = LoadCursor(NULL, IDC_WAIT);
 409 
 410     wndClass = RegisterClassEx(&wcex);
 411     if (!wndClass) {
 412         return 0;
 413     }
 414 
 415     splash->x = (GetSystemMetrics(SM_CXSCREEN) - splash->width) / 2;
 416     splash->y = (GetSystemMetrics(SM_CYSCREEN) - splash->height) / 2;
 417     exStyle = splash->isLayered ? WS_EX_LAYERED : 0;
 418     exStyle |= WS_EX_TOOLWINDOW;        /* don't show the window on taskbar */
 419     style = WS_POPUP;
 420     hWnd = CreateWindowEx(exStyle, (LPCSTR) wndClass, "", style,
 421             splash->x, splash->y, splash->width, splash->height, NULL, NULL,
 422             wcex.hInstance, NULL);
 423     SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) splash);
 424     return hWnd;
 425 }
 426 
 427 void
 428 SplashLock(Splash * splash)
 429 {
 430     EnterCriticalSection(&splash->lock);
 431 }
 432 
 433 void
 434 SplashUnlock(Splash * splash)
 435 {
 436     LeaveCriticalSection(&splash->lock);
 437 }
 438 
 439 void
 440 SplashInitPlatform(Splash * splash)
 441 {
 442     HDC hdc;
 443     int paletteMode;
 444 
 445     InitializeCriticalSection(&splash->lock);
 446     splash->isLayered = FALSE;
 447     hdc = GetDC(NULL);
 448     paletteMode = (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) != 0;
 449     if (UpdateLayeredWindow && !paletteMode) {
 450         splash->isLayered = TRUE;
 451     }
 452     splash->byteAlignment = 4;
 453     if (splash->isLayered) {
 454         initFormat(&splash->screenFormat,
 455                 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
 456         splash->screenFormat.premultiplied = 1;
 457         splash->maskRequired = 0;
 458     }
 459     else {
 460         splash->maskRequired = 1;
 461         if (paletteMode) {
 462             int numColors = GetDeviceCaps(hdc, SIZEPALETTE) -
 463                 GetDeviceCaps(hdc, NUMRESERVED);
 464             int i;
 465             int numComponents[3];
 466 
 467             initFormat(&splash->screenFormat, 0, 0, 0, 0);
 468             /*      FIXME: maybe remapping to non-reserved colors would improve performance */
 469             for (i = 0; i < numColors; i++) {
 470                 splash->colorIndex[i] = i;
 471             }
 472             numColors = quantizeColors(numColors, numComponents);
 473             initColorCube(numComponents, splash->colorMap, splash->dithers,
 474                     splash->colorIndex);
 475             splash->screenFormat.colorIndex = splash->colorIndex;
 476             splash->screenFormat.depthBytes = 1;
 477             splash->screenFormat.colorMap = splash->colorMap;
 478             splash->screenFormat.dithers = splash->dithers;
 479             splash->screenFormat.numColors = numColors;
 480             splash->hPalette = NULL;
 481         }
 482         else {
 483             initFormat(&splash->screenFormat,
 484                     0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
 485         }
 486     }
 487     ReleaseDC(NULL, hdc);
 488 }
 489 
 490 void
 491 SplashCleanupPlatform(Splash * splash)
 492 {
 493     int i;
 494 
 495     if (splash->frames) {
 496         for (i = 0; i < splash->frameCount; i++) {
 497             if (splash->frames[i].hRgn) {
 498                 DeleteObject(splash->frames[i].hRgn);
 499                 splash->frames[i].hRgn = NULL;
 500             }
 501         }
 502     }
 503     if (splash->hPalette)
 504         DeleteObject(splash->hPalette);
 505     splash->maskRequired = !splash->isLayered;
 506 }
 507 
 508 void
 509 SplashDonePlatform(Splash * splash)
 510 {
 511     if (splash->hWnd)
 512         DestroyWindow(splash->hWnd);
 513 }
 514 
 515 void
 516 SplashMessagePump()
 517 {
 518     MSG msg;
 519 
 520     while (GetMessage(&msg, NULL, 0, 0)) {
 521         TranslateMessage(&msg);
 522         DispatchMessage(&msg);
 523     }
 524 }
 525 
 526 DWORD WINAPI
 527 SplashScreenThread(LPVOID param)
 528 {
 529     Splash *splash = (Splash *) param;
 530 
 531     splash->currentFrame = 0;
 532     SplashLock(splash);
 533     splash->time = SplashTime();
 534     splash->hWnd = SplashCreateWindow(splash);
 535     if (splash->hWnd) {
 536         SplashRedrawWindow(splash);
 537         SplashUnlock(splash);
 538         SplashMessagePump();
 539         SplashLock(splash);
 540     }
 541     SplashDone(splash);
 542     splash->isVisible = -1;
 543     SplashUnlock(splash);
 544     return 0;
 545 }
 546 
 547 void
 548 SplashCreateThread(Splash * splash)
 549 {
 550     DWORD threadId;
 551 
 552     CreateThread(NULL, 0, SplashScreenThread, (LPVOID) splash, 0, &threadId);
 553 }
 554 
 555 void
 556 SplashClosePlatform(Splash * splash)
 557 {
 558     PostMessage(splash->hWnd, WM_QUIT, 0, 0);
 559 }
 560 
 561 void
 562 SplashUpdate(Splash * splash)
 563 {
 564     PostMessage(splash->hWnd, WM_SPLASHUPDATE, 0, 0);
 565 }
 566 
 567 void
 568 SplashReconfigure(Splash * splash)
 569 {
 570     PostMessage(splash->hWnd, WM_SPLASHRECONFIGURE, 0, 0);
 571 }
 572 
 573 SPLASHEXPORT char*
 574 SplashGetScaledImageName(const char* jarName, const char* fileName,
 575                            float *scaleFactor)
 576 {
 577     float dpiScaleX = -1.0f;
 578     float dpiScaleY = -1.0f;
 579     *scaleFactor = 1.0;
 580     GetScreenDpi(getPrimaryMonitor(), &dpiScaleX, &dpiScaleY);
 581     *scaleFactor = dpiScaleX > 0 ? dpiScaleX / 96 : *scaleFactor;
 582 
 583     if (*scaleFactor > 1.0) {
 584         char strDpi[BUFF_SIZE];
 585         _snprintf(strDpi, BUFF_SIZE, "%d", (int)dpiScaleX);
 586         char *dupFileName = strdup(fileName);
 587         char *fileExtension = strrchr(dupFileName, '.');
 588         char *scaledImgName = NULL;
 589         char *nameToAppend = ".scale-";
 590         size_t length = 0;
 591         /*File is missing extension */
 592         if (fileExtension == NULL) {
 593             length = strlen(dupFileName) + strlen(nameToAppend) +
 594                 strlen(strDpi) + 1;
 595             scaledImgName = SAFE_SIZE_ARRAY_ALLOC(malloc, length, sizeof(char));
 596             int retVal = _snprintf(scaledImgName, length, "%s%s%s", dupFileName,
 597                 nameToAppend, strDpi);
 598             if (retVal < 0 || (retVal != length - 1)) {
 599                 *scaleFactor = 1;
 600                 free(dupFileName);
 601                 free(scaledImgName);
 602                 return NULL;
 603             }
 604         }
 605         else {
 606             size_t length_Without_Ext = fileExtension - dupFileName;
 607             length = length_Without_Ext + strlen(nameToAppend) + strlen(strDpi) +
 608                 strlen(fileExtension) + 1;
 609             scaledImgName = SAFE_SIZE_ARRAY_ALLOC(malloc, length, sizeof(char));
 610             int retVal = _snprintf(scaledImgName, length, "%.*s%s%s%s",
 611                 length_Without_Ext, dupFileName, nameToAppend, strDpi, fileExtension);
 612             if (retVal < 0 || (retVal != length - 1)) {
 613                 *scaleFactor = 1;
 614                 free(dupFileName);
 615                 free(scaledImgName);
 616                 return NULL;
 617             }
 618         }
 619         free(dupFileName);
 620         FILE *fp = NULL;
 621         if (!(fp = fopen(scaledImgName, "r"))) {
 622             *scaleFactor = 1;
 623             free(scaledImgName);
 624             return NULL;
 625         }
 626         fclose(fp);
 627         return scaledImgName;
 628     }
 629     return NULL;
 630 }