1 /*
2  * Copyright (c) 2005, 2019, 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 #include "splashscreen_impl.h"
27 #include "splashscreen_gfx_impl.h"
28 #define BUFF_SIZE 1024
29 #ifdef _MSC_VER
30 # ifndef snprintf
31 #       define snprintf _snprintf
32 # endif
33 #endif
34 int splashIsVisible = 0;
35 
36 Splash *
SplashGetInstance()37 SplashGetInstance()
38 {
39     static Splash splash;
40     static int preInitialized = 0;
41     if (!preInitialized) {
42         memset(&splash, 0, sizeof(Splash));
43         splash.currentFrame = -1;
44         preInitialized = 1;
45     }
46     return &splash;
47 }
48 
49 JNIEXPORT void
SplashSetFileJarName(const char * fileName,const char * jarName)50 SplashSetFileJarName(const char* fileName, const char* jarName) {
51     Splash *splash = SplashGetInstance();
52 
53     free(splash->fileName);
54     splash->fileName = SplashConvertStringAlloc(fileName, &splash->fileNameLen);
55 
56     free(splash->jarName);
57     splash->jarName = SplashConvertStringAlloc(jarName, &splash->jarNameLen);
58 }
59 
60 JNIEXPORT void
SplashInit()61 SplashInit()
62 {
63     Splash *splash = SplashGetInstance();
64 
65     memset(splash, 0, sizeof(Splash));
66     splash->currentFrame = -1;
67     splash->scaleFactor = 1;
68     initFormat(&splash->imageFormat, QUAD_RED_MASK, QUAD_GREEN_MASK,
69         QUAD_BLUE_MASK, QUAD_ALPHA_MASK);
70     SplashInitPlatform(splash);
71 }
72 
73 JNIEXPORT void
SplashClose()74 SplashClose()
75 {
76     Splash *splash = SplashGetInstance();
77 
78     if (splash->isVisible > 0) {
79         SplashLock(splash);
80         splash->isVisible = -1;
81         SplashClosePlatform(splash);
82         SplashUnlock(splash);
83     }
84 }
85 
86 void
SplashCleanup(Splash * splash)87 SplashCleanup(Splash * splash)
88 {
89     int i;
90 
91     splash->currentFrame = -1;
92     SplashCleanupPlatform(splash);
93     if (splash->frames) {
94         for (i = 0; i < splash->frameCount; i++) {
95             if (splash->frames[i].bitmapBits) {
96                 free(splash->frames[i].bitmapBits);
97                 splash->frames[i].bitmapBits = NULL;
98             }
99         }
100         free(splash->frames);
101         splash->frames = NULL;
102     }
103     if (splash->overlayData) {
104         free(splash->overlayData);
105         splash->overlayData = NULL;
106     }
107     SplashSetFileJarName(NULL, NULL);
108 }
109 
110 JNIEXPORT void
SplashSetScaleFactor(float scaleFactor)111 SplashSetScaleFactor(float scaleFactor)
112 {
113     Splash *splash = SplashGetInstance();
114     splash->scaleFactor = scaleFactor;
115 }
116 
117 void
SplashDone(Splash * splash)118 SplashDone(Splash * splash)
119 {
120     SplashCleanup(splash);
121     SplashDonePlatform(splash);
122 }
123 
124 int
SplashIsStillLooping(Splash * splash)125 SplashIsStillLooping(Splash * splash)
126 {
127     if (splash->currentFrame < 0) {
128         return 0;
129     }
130     return splash->loopCount != 1 ||
131         splash->currentFrame + 1 < splash->frameCount;
132 }
133 
134 void
SplashUpdateScreenData(Splash * splash)135 SplashUpdateScreenData(Splash * splash)
136 {
137     ImageRect srcRect, dstRect;
138     if (splash->currentFrame < 0) {
139         return;
140     }
141 
142     initRect(&srcRect, 0, 0, splash->width, splash->height, 1,
143         splash->width * sizeof(rgbquad_t),
144         splash->frames[splash->currentFrame].bitmapBits, &splash->imageFormat);
145     if (splash->screenData) {
146         free(splash->screenData);
147     }
148     splash->screenStride = splash->width * splash->screenFormat.depthBytes;
149     if (splash->byteAlignment > 1) {
150         splash->screenStride =
151             (splash->screenStride + splash->byteAlignment - 1) &
152             ~(splash->byteAlignment - 1);
153     }
154     splash->screenData = malloc(splash->height * splash->screenStride);
155     initRect(&dstRect, 0, 0, splash->width, splash->height, 1,
156         splash->screenStride, splash->screenData, &splash->screenFormat);
157     if (splash->overlayData) {
158         convertRect2(&srcRect, &dstRect, CVT_BLEND, &splash->overlayRect);
159     }
160     else {
161         convertRect(&srcRect, &dstRect, CVT_COPY);
162     }
163 }
164 
165 void
SplashNextFrame(Splash * splash)166 SplashNextFrame(Splash * splash)
167 {
168     if (splash->currentFrame < 0) {
169         return;
170     }
171     do {
172         if (!SplashIsStillLooping(splash)) {
173             return;
174         }
175         splash->time += splash->frames[splash->currentFrame].delay;
176         if (++splash->currentFrame >= splash->frameCount) {
177             splash->currentFrame = 0;
178             if (splash->loopCount > 0) {
179                 splash->loopCount--;
180             }
181         }
182     } while (splash->time + splash->frames[splash->currentFrame].delay -
183         SplashTime() <= 0);
184 }
185 
186 int
BitmapToYXBandedRectangles(ImageRect * pSrcRect,RECT_T * out)187 BitmapToYXBandedRectangles(ImageRect * pSrcRect, RECT_T * out)
188 {
189     RECT_T *pPrevLine = NULL, *pFirst = out, *pThis = pFirst;
190     int i, j, i0;
191     int length;
192 
193     for (j = 0; j < pSrcRect->numLines; j++) {
194 
195         /* generate data for a scanline */
196 
197         byte_t *pSrc = (byte_t *) pSrcRect->pBits + j * pSrcRect->stride;
198         RECT_T *pLine = pThis;
199 
200         i = 0;
201 
202         do {
203             while (i < pSrcRect->numSamples &&
204                    getRGBA(pSrc, pSrcRect->format) < ALPHA_THRESHOLD) {
205                 pSrc += pSrcRect->depthBytes;
206                 ++i;
207             }
208             if (i >= pSrcRect->numSamples) {
209                 break;
210             }
211             i0 = i;
212             while (i < pSrcRect->numSamples &&
213                    getRGBA(pSrc, pSrcRect->format) >= ALPHA_THRESHOLD) {
214                 pSrc += pSrcRect->depthBytes;
215                 ++i;
216             }
217             RECT_SET(*pThis, i0, j, i - i0, 1);
218             ++pThis;
219         } while (i < pSrcRect->numSamples);
220 
221         /*  check if the previous scanline is exactly the same, merge if so
222            (this is the only optimization we can use for YXBanded rectangles, and win32 supports
223            YXBanded only */
224 
225         length = pThis - pLine;
226         if (pPrevLine && pLine - pPrevLine == length) {
227             for (i = 0; i < length && RECT_EQ_X(pPrevLine[i], pLine[i]); ++i) {
228             }
229             if (i == pLine - pPrevLine) {
230                 // do merge
231                 for (i = 0; i < length; i++) {
232                     RECT_INC_HEIGHT(pPrevLine[i]);
233                 }
234                 pThis = pLine;
235                 continue;
236             }
237         }
238         /* or else use the generated scanline */
239 
240         pPrevLine = pLine;
241     }
242     return pThis - pFirst;
243 }
244 
245 typedef struct FILEFORMAT
246 {
247     int sign;
248     int (*decodeStream) (Splash * splash, SplashStream * stream);
249 } FILEFORMAT;
250 
251 static const FILEFORMAT formats[] = {
252     {0x47, SplashDecodeGifStream},
253     {0x89, SplashDecodePngStream},
254     {0xFF, SplashDecodeJpegStream}
255 };
256 
257 static int
SplashLoadStream(SplashStream * stream)258 SplashLoadStream(SplashStream * stream)
259 {
260     int success = 0;
261     int c;
262     size_t i;
263 
264     Splash *splash = SplashGetInstance();
265     if (splash->isVisible < 0) {
266         return 0;
267     }
268 
269     SplashLock(splash);
270 
271     /* the formats we support can be easily distinguished by the first byte */
272     c = stream->peek(stream);
273     if (c != -1) {
274         for (i = 0; i < sizeof(formats) / sizeof(FILEFORMAT); i++) {
275             if (c == formats[i].sign) {
276                 success = formats[i].decodeStream(splash, stream);
277                 break;
278             }
279         }
280     }
281     stream->close(stream);
282 
283     if (!success) {             // failed to decode
284         if (splash->isVisible == 0) {
285             SplashCleanup(splash);
286         }
287         SplashUnlock(splash);   // SplashClose locks
288         if (splash->isVisible == 0) {
289             SplashClose();
290         }
291     }
292     else {
293         splash->currentFrame = 0;
294         if (splash->isVisible == 0) {
295             SplashStart(splash);
296         } else {
297             SplashReconfigure(splash);
298             splash->time = SplashTime();
299         }
300         SplashUnlock(splash);
301     }
302     return success;
303 }
304 
305 JNIEXPORT int
SplashLoadFile(const char * filename)306 SplashLoadFile(const char *filename)
307 {
308     SplashStream stream;
309     return SplashStreamInitFile(&stream, filename) &&
310                 SplashLoadStream(&stream);
311 }
312 
313 JNIEXPORT int
SplashLoadMemory(void * data,int size)314 SplashLoadMemory(void *data, int size)
315 {
316     SplashStream stream;
317     return SplashStreamInitMemory(&stream, data, size) &&
318                 SplashLoadStream(&stream);
319 }
320 
321 /* SplashStart MUST be called from under the lock */
322 
323 void
SplashStart(Splash * splash)324 SplashStart(Splash * splash)
325 {
326     if (splash->isVisible == 0) {
327         SplashCreateThread(splash);
328         splash->isVisible = 1;
329     }
330 }
331 
332 /* SplashStream functions */
333 
readFile(void * pStream,void * pData,int nBytes)334 static int readFile(void* pStream, void* pData, int nBytes) {
335     FILE* f = ((SplashStream*)pStream)->arg.stdio.f;
336     return fread(pData, 1, nBytes, f);
337 }
peekFile(void * pStream)338 static int peekFile(void* pStream) {
339     FILE* f = ((SplashStream*)pStream)->arg.stdio.f;
340     int c = fgetc(f);
341     if (c != EOF) {
342         ungetc(c, f);
343         return c;
344     } else {
345         return -1;
346     }
347 }
348 
closeFile(void * pStream)349 static void closeFile(void* pStream) {
350     FILE* f = ((SplashStream*)pStream)->arg.stdio.f;
351     fclose(f);
352 }
353 
readMem(void * pStream,void * pData,int nBytes)354 static int readMem(void* pStream, void* pData, int nBytes) {
355     unsigned char* pSrc = (unsigned char*)(((SplashStream*)pStream)->arg.mem.pData);
356     unsigned char* pSrcEnd = (unsigned char*)(((SplashStream*)pStream)->arg.mem.pDataEnd);
357     if (nBytes > pSrcEnd - pSrc) {
358         nBytes = pSrcEnd - pSrc;
359     }
360     if (nBytes>0) {
361         memcpy(pData, pSrc, nBytes);
362         pSrc += nBytes;
363         ((SplashStream*)pStream)->arg.mem.pData = (void*)pSrc;
364     }
365     return nBytes;
366 }
367 
peekMem(void * pStream)368 static int peekMem(void* pStream) {
369     unsigned char* pSrc = (unsigned char*)(((SplashStream*)pStream)->arg.mem.pData);
370     unsigned char* pSrcEnd = (unsigned char*)(((SplashStream*)pStream)->arg.mem.pDataEnd);
371     if (pSrc >= pSrcEnd) {
372         return -1;
373     } else {
374         return (int)*pSrc;
375     }
376 }
377 
closeMem(void * pStream)378 static void closeMem(void* pStream) {
379 }
380 
SplashStreamInitFile(SplashStream * pStream,const char * filename)381 int SplashStreamInitFile(SplashStream * pStream, const char* filename) {
382     pStream->arg.stdio.f = fopen(filename, "rb");
383     pStream->read = readFile;
384     pStream->peek = peekFile;
385     pStream->close = closeFile;
386     return pStream->arg.stdio.f != 0;
387 }
388 
SplashStreamInitMemory(SplashStream * pStream,void * pData,int size)389 int SplashStreamInitMemory(SplashStream * pStream, void* pData, int size) {
390     pStream->arg.mem.pData = (unsigned char*)pData;
391     pStream->arg.mem.pDataEnd = (unsigned char*)pData + size;
392     pStream->read = readMem;
393     pStream->peek = peekMem;
394     pStream->close = closeMem;
395     return 1;
396 }
397 
398 JNIEXPORT int
SplashGetScaledImgNameMaxPstfixLen(const char * fileName)399 SplashGetScaledImgNameMaxPstfixLen(const char *fileName){
400     return strlen(fileName) + strlen("@100pct") + 1;
401 }
402 
GetScaledImageName(const char * fileName,char * scaleImageName,float * scaleFactor,const size_t scaledImageLength)403 jboolean GetScaledImageName(const char *fileName, char *scaleImageName,
404         float *scaleFactor, const size_t scaledImageLength) {
405     if (*scaleFactor > 1.0) {
406         FILE *fp = NULL;
407         char scaledImgPct[BUFF_SIZE];
408         char scaledImgX[BUFF_SIZE];
409         char *scaledImageXName = NULL;
410         char *scaledImagePctName = malloc(scaledImageLength);
411         char *dupFileName = strdup(fileName);
412         char *fileExtension = strrchr(dupFileName, '.');
413         size_t lengthPct = 0;
414         size_t lengthX = 0;
415         int retValPct = 0;
416         int retValX = 0;
417         jboolean isPctScaledImage = (*scaleFactor * 100) != ((int) (*scaleFactor)) *100;
418         snprintf(scaledImgPct, BUFF_SIZE, "%s%d%s", "@",
419                 (int) (*scaleFactor * 100), "pct");
420         if (!isPctScaledImage) {
421             scaledImageXName = malloc(scaledImageLength);
422             snprintf(scaledImgX, BUFF_SIZE, "%s%d%s", "@", (int) (*scaleFactor), "x");
423         }
424         /*File is missing extension */
425         if (fileExtension == NULL) {
426             lengthPct = strlen(dupFileName) +
427                     strlen(scaledImgPct) + 1;
428             if (!isPctScaledImage) {
429                 lengthX = strlen(dupFileName) +
430                         strlen(scaledImgX) + 1;
431             }
432             if (lengthPct > scaledImageLength || lengthX > scaledImageLength) {
433                 cleanUp(dupFileName, scaledImageXName, scaledImagePctName, scaleFactor);
434                 return JNI_FALSE;
435             }
436             retValPct = snprintf(scaledImagePctName, lengthPct, "%s%s", dupFileName,
437                     scaledImgPct);
438             if (!isPctScaledImage) {
439                 retValX = snprintf(scaledImageXName, lengthX, "%s%s", dupFileName,
440                         scaledImgX);
441             }
442             if ((retValPct < 0 || (retValPct > lengthPct - 1)) ||
443                     (retValX < 0 || (retValX > lengthX - 1))) {
444                 cleanUp(dupFileName, scaledImageXName, scaledImagePctName, scaleFactor);
445                 return JNI_FALSE;
446             }
447         } else {
448             int length_Without_Ext = fileExtension - dupFileName;
449             lengthPct = length_Without_Ext + strlen(scaledImgPct) +
450                     strlen(fileExtension) + 1;
451             if (!isPctScaledImage) {
452                 lengthX = length_Without_Ext + strlen(scaledImgX) +
453                         strlen(fileExtension) + 1;
454             }
455             if (lengthPct > scaledImageLength || lengthX > scaledImageLength) {
456                 cleanUp(dupFileName, scaledImageXName, scaledImagePctName, scaleFactor);
457                 return JNI_FALSE;
458             }
459             retValPct = snprintf(scaledImagePctName, lengthPct, "%.*s%s%s",
460                     length_Without_Ext, dupFileName, scaledImgPct, fileExtension);
461             if (!isPctScaledImage) {
462                 retValX = snprintf(scaledImageXName, lengthX, "%.*s%s%s",
463                         length_Without_Ext, dupFileName, scaledImgX, fileExtension);
464             }
465             if ((retValPct < 0 || (retValPct > lengthPct - 1)) ||
466                     (retValX < 0 || (retValX > lengthX - 1))) {
467                 cleanUp(dupFileName, scaledImageXName, scaledImagePctName, scaleFactor);
468                 return JNI_FALSE;
469             }
470         }
471         free(dupFileName);
472         if (!(fp = fopen(scaledImagePctName, "r"))) {
473             if (!isPctScaledImage && (fp = fopen(scaledImageXName, "r"))) {
474                 fclose(fp);
475                 strcpy(scaleImageName, scaledImageXName);
476                 free(scaledImageXName);
477                 free(scaledImagePctName);
478                 return JNI_TRUE;
479             }
480             cleanUp(NULL, scaledImageXName, scaledImagePctName, scaleFactor);
481             return JNI_FALSE;
482         }
483         fclose(fp);
484         strcpy(scaleImageName, scaledImagePctName);
485         free(scaledImageXName);
486         free(scaledImagePctName);
487         return JNI_TRUE;
488     }
489     return JNI_FALSE;
490 }
491 
cleanUp(char * fName,char * xName,char * pctName,float * scaleFactor)492 void cleanUp(char *fName, char *xName, char *pctName, float *scaleFactor) {
493     *scaleFactor = 1;
494     free(fName);
495     free(xName);
496     free(pctName);
497 }
498