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
34int splashIsVisible = 0;
35
36Splash *
37SplashGetInstance()
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
49JNIEXPORT void
50SplashSetFileJarName(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
60JNIEXPORT int
61SplashInit()
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
73JNIEXPORT void
74SplashClose()
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
86void
87SplashCleanup(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
110JNIEXPORT void
111SplashSetScaleFactor(float scaleFactor)
112{
113 Splash *splash = SplashGetInstance();
114 splash->scaleFactor = scaleFactor;
115}
116
117void
118SplashDone(Splash * splash)
119{
120 SplashCleanup(splash);
121 SplashDonePlatform(splash);
122}
123
124int
125SplashIsStillLooping(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
134void
135SplashUpdateScreenData(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
165void
166SplashNextFrame(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
186int
187BitmapToYXBandedRectangles(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
245typedef struct FILEFORMAT
246{
247 int sign;
248 int (*decodeStream) (Splash * splash, SplashStream * stream);
249} FILEFORMAT;
250
251static const FILEFORMAT formats[] = {
252 {0x47, SplashDecodeGifStream},
253 {0x89, SplashDecodePngStream},
254 {0xFF, SplashDecodeJpegStream}
255};
256
257static int
258SplashLoadStream(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
306JNIEXPORT int
307SplashLoadFile(const char *filename)
308{
309 SplashStream stream;
310 return SplashStreamInitFile(&stream, filename) &&
311 SplashLoadStream(&stream);
312}
313
314JNIEXPORT int
315SplashLoadMemory(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
324void
325SplashStart(Splash * splash)
326{
327 if (splash->isVisible == 0) {
328 SplashCreateThread(splash);
329 splash->isVisible = 1;
330 }
331}
332
333/* SplashStream functions */
334
335static int readFile(void* pStream, void* pData, int nBytes) {
336 FILE* f = ((SplashStream*)pStream)->arg.stdio.f;
337 return fread(pData, 1, nBytes, f);
338}
339static 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
350static void closeFile(void* pStream) {
351 FILE* f = ((SplashStream*)pStream)->arg.stdio.f;
352 fclose(f);
353}
354
355static 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
369static 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
379static void closeMem(void* pStream) {
380}
381
382int 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
390int 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
399JNIEXPORT int
400SplashGetScaledImgNameMaxPstfixLen(const char *fileName){
401 return strlen(fileName) + strlen("@100pct") + 1;
402}
403
404jboolean 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
493void cleanUp(char *fName, char *xName, char *pctName, float *scaleFactor) {
494 *scaleFactor = 1;
495 free(fName);
496 free(xName);
497 free(pctName);
498}
499