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