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 * |
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 |
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 |
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 |
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 |
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 |
111 | SplashSetScaleFactor(float scaleFactor) |
112 | { |
113 | Splash *splash = SplashGetInstance(); |
114 | splash->scaleFactor = scaleFactor; |
115 | } |
116 | |
117 | void |
118 | SplashDone(Splash * splash) |
119 | { |
120 | SplashCleanup(splash); |
121 | SplashDonePlatform(splash); |
122 | } |
123 | |
124 | int |
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 |
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 |
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 |
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 |
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 |
307 | SplashLoadFile(const char *filename) |
308 | { |
309 | SplashStream stream; |
310 | return SplashStreamInitFile(&stream, filename) && |
311 | SplashLoadStream(&stream); |
312 | } |
313 | |
314 | JNIEXPORT int |
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 |
325 | SplashStart(Splash * splash) |
326 | { |
327 | if (splash->isVisible == 0) { |
328 | SplashCreateThread(splash); |
329 | splash->isVisible = 1; |
330 | } |
331 | } |
332 | |
333 | /* SplashStream functions */ |
334 | |
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 | } |
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 | |
350 | static void closeFile(void* pStream) { |
351 | FILE* f = ((SplashStream*)pStream)->arg.stdio.f; |
352 | fclose(f); |
353 | } |
354 | |
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 | |
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 | |
379 | static void closeMem(void* pStream) { |
380 | } |
381 | |
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 | |
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 |
400 | SplashGetScaledImgNameMaxPstfixLen(const char *fileName){ |
401 | return strlen(fileName) + strlen("@100pct" ) + 1; |
402 | } |
403 | |
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 | |
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 | |