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 int
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     return 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         stream->close(stream);
267         return 0;
268     }
269 
270     SplashLock(splash);
271 
272     /* the formats we support can be easily distinguished by the first byte */
273     c = stream->peek(stream);
274     if (c != -1) {
275         for (i = 0; i < sizeof(formats) / sizeof(FILEFORMAT); i++) {
276             if (c == formats[i].sign) {
277                 success = formats[i].decodeStream(splash, stream);
278                 break;
279             }
280         }
281     }
282     stream->close(stream);
283 
284     if (!success) {             // failed to decode
285         if (splash->isVisible == 0) {
286             SplashCleanup(splash);
287         }
288         SplashUnlock(splash);   // SplashClose locks
289         if (splash->isVisible == 0) {
290             SplashClose();
291         }
292     }
293     else {
294         splash->currentFrame = 0;
295         if (splash->isVisible == 0) {
296             SplashStart(splash);
297         } else {
298             SplashReconfigure(splash);
299             splash->time = SplashTime();
300         }
301         SplashUnlock(splash);
302     }
303     return success;
304 }
305 
306 JNIEXPORT int
SplashLoadFile(const char * filename)307 SplashLoadFile(const char *filename)
308 {
309     SplashStream stream;
310     return SplashStreamInitFile(&stream, filename) &&
311                 SplashLoadStream(&stream);
312 }
313 
314 JNIEXPORT int
SplashLoadMemory(void * data,int size)315 SplashLoadMemory(void *data, int size)
316 {
317     SplashStream stream;
318     return SplashStreamInitMemory(&stream, data, size) &&
319                 SplashLoadStream(&stream);
320 }
321 
322 /* SplashStart MUST be called from under the lock */
323 
324 void
SplashStart(Splash * splash)325 SplashStart(Splash * splash)
326 {
327     if (splash->isVisible == 0) {
328         SplashCreateThread(splash);
329         splash->isVisible = 1;
330     }
331 }
332 
333 /* SplashStream functions */
334 
readFile(void * pStream,void * pData,int nBytes)335 static int readFile(void* pStream, void* pData, int nBytes) {
336     FILE* f = ((SplashStream*)pStream)->arg.stdio.f;
337     return fread(pData, 1, nBytes, f);
338 }
peekFile(void * pStream)339 static int peekFile(void* pStream) {
340     FILE* f = ((SplashStream*)pStream)->arg.stdio.f;
341     int c = fgetc(f);
342     if (c != EOF) {
343         ungetc(c, f);
344         return c;
345     } else {
346         return -1;
347     }
348 }
349 
closeFile(void * pStream)350 static void closeFile(void* pStream) {
351     FILE* f = ((SplashStream*)pStream)->arg.stdio.f;
352     fclose(f);
353 }
354 
readMem(void * pStream,void * pData,int nBytes)355 static int readMem(void* pStream, void* pData, int nBytes) {
356     unsigned char* pSrc = (unsigned char*)(((SplashStream*)pStream)->arg.mem.pData);
357     unsigned char* pSrcEnd = (unsigned char*)(((SplashStream*)pStream)->arg.mem.pDataEnd);
358     if (nBytes > pSrcEnd - pSrc) {
359         nBytes = pSrcEnd - pSrc;
360     }
361     if (nBytes>0) {
362         memcpy(pData, pSrc, nBytes);
363         pSrc += nBytes;
364         ((SplashStream*)pStream)->arg.mem.pData = (void*)pSrc;
365     }
366     return nBytes;
367 }
368 
peekMem(void * pStream)369 static int peekMem(void* pStream) {
370     unsigned char* pSrc = (unsigned char*)(((SplashStream*)pStream)->arg.mem.pData);
371     unsigned char* pSrcEnd = (unsigned char*)(((SplashStream*)pStream)->arg.mem.pDataEnd);
372     if (pSrc >= pSrcEnd) {
373         return -1;
374     } else {
375         return (int)*pSrc;
376     }
377 }
378 
closeMem(void * pStream)379 static void closeMem(void* pStream) {
380 }
381 
SplashStreamInitFile(SplashStream * pStream,const char * filename)382 int SplashStreamInitFile(SplashStream * pStream, const char* filename) {
383     pStream->arg.stdio.f = fopen(filename, "rb");
384     pStream->read = readFile;
385     pStream->peek = peekFile;
386     pStream->close = closeFile;
387     return pStream->arg.stdio.f != 0;
388 }
389 
SplashStreamInitMemory(SplashStream * pStream,void * pData,int size)390 int SplashStreamInitMemory(SplashStream * pStream, void* pData, int size) {
391     pStream->arg.mem.pData = (unsigned char*)pData;
392     pStream->arg.mem.pDataEnd = (unsigned char*)pData + size;
393     pStream->read = readMem;
394     pStream->peek = peekMem;
395     pStream->close = closeMem;
396     return 1;
397 }
398 
399 JNIEXPORT int
SplashGetScaledImgNameMaxPstfixLen(const char * fileName)400 SplashGetScaledImgNameMaxPstfixLen(const char *fileName){
401     return strlen(fileName) + strlen("@100pct") + 1;
402 }
403 
GetScaledImageName(const char * fileName,char * scaleImageName,float * scaleFactor,const size_t scaledImageLength)404 jboolean GetScaledImageName(const char *fileName, char *scaleImageName,
405         float *scaleFactor, const size_t scaledImageLength) {
406     if (*scaleFactor > 1.0) {
407         FILE *fp = NULL;
408         char scaledImgPct[BUFF_SIZE];
409         char scaledImgX[BUFF_SIZE];
410         char *scaledImageXName = NULL;
411         char *scaledImagePctName = malloc(scaledImageLength);
412         char *dupFileName = strdup(fileName);
413         char *fileExtension = strrchr(dupFileName, '.');
414         size_t lengthPct = 0;
415         size_t lengthX = 0;
416         int retValPct = 0;
417         int retValX = 0;
418         jboolean isPctScaledImage = (*scaleFactor * 100) != ((int) (*scaleFactor)) *100;
419         snprintf(scaledImgPct, BUFF_SIZE, "%s%d%s", "@",
420                 (int) (*scaleFactor * 100), "pct");
421         if (!isPctScaledImage) {
422             scaledImageXName = malloc(scaledImageLength);
423             snprintf(scaledImgX, BUFF_SIZE, "%s%d%s", "@", (int) (*scaleFactor), "x");
424         }
425         /*File is missing extension */
426         if (fileExtension == NULL) {
427             lengthPct = strlen(dupFileName) +
428                     strlen(scaledImgPct) + 1;
429             if (!isPctScaledImage) {
430                 lengthX = strlen(dupFileName) +
431                         strlen(scaledImgX) + 1;
432             }
433             if (lengthPct > scaledImageLength || lengthX > scaledImageLength) {
434                 cleanUp(dupFileName, scaledImageXName, scaledImagePctName, scaleFactor);
435                 return JNI_FALSE;
436             }
437             retValPct = snprintf(scaledImagePctName, lengthPct, "%s%s", dupFileName,
438                     scaledImgPct);
439             if (!isPctScaledImage) {
440                 retValX = snprintf(scaledImageXName, lengthX, "%s%s", dupFileName,
441                         scaledImgX);
442             }
443             if ((retValPct < 0 || (retValPct > lengthPct - 1)) ||
444                     (retValX < 0 || (retValX > lengthX - 1))) {
445                 cleanUp(dupFileName, scaledImageXName, scaledImagePctName, scaleFactor);
446                 return JNI_FALSE;
447             }
448         } else {
449             int length_Without_Ext = fileExtension - dupFileName;
450             lengthPct = length_Without_Ext + strlen(scaledImgPct) +
451                     strlen(fileExtension) + 1;
452             if (!isPctScaledImage) {
453                 lengthX = length_Without_Ext + strlen(scaledImgX) +
454                         strlen(fileExtension) + 1;
455             }
456             if (lengthPct > scaledImageLength || lengthX > scaledImageLength) {
457                 cleanUp(dupFileName, scaledImageXName, scaledImagePctName, scaleFactor);
458                 return JNI_FALSE;
459             }
460             retValPct = snprintf(scaledImagePctName, lengthPct, "%.*s%s%s",
461                     length_Without_Ext, dupFileName, scaledImgPct, fileExtension);
462             if (!isPctScaledImage) {
463                 retValX = snprintf(scaledImageXName, lengthX, "%.*s%s%s",
464                         length_Without_Ext, dupFileName, scaledImgX, fileExtension);
465             }
466             if ((retValPct < 0 || (retValPct > lengthPct - 1)) ||
467                     (retValX < 0 || (retValX > lengthX - 1))) {
468                 cleanUp(dupFileName, scaledImageXName, scaledImagePctName, scaleFactor);
469                 return JNI_FALSE;
470             }
471         }
472         free(dupFileName);
473         if (!(fp = fopen(scaledImagePctName, "r"))) {
474             if (!isPctScaledImage && (fp = fopen(scaledImageXName, "r"))) {
475                 fclose(fp);
476                 strcpy(scaleImageName, scaledImageXName);
477                 free(scaledImageXName);
478                 free(scaledImagePctName);
479                 return JNI_TRUE;
480             }
481             cleanUp(NULL, scaledImageXName, scaledImagePctName, scaleFactor);
482             return JNI_FALSE;
483         }
484         fclose(fp);
485         strcpy(scaleImageName, scaledImagePctName);
486         free(scaledImageXName);
487         free(scaledImagePctName);
488         return JNI_TRUE;
489     }
490     return JNI_FALSE;
491 }
492 
cleanUp(char * fName,char * xName,char * pctName,float * scaleFactor)493 void cleanUp(char *fName, char *xName, char *pctName, float *scaleFactor) {
494     *scaleFactor = 1;
495     free(fName);
496     free(xName);
497     free(pctName);
498 }
499