imgldr.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. #include <precomp.h>
  2. #include <setjmp.h>
  3. #include <bfc/bfc_assert.h>
  4. #include <api.h>
  5. #include <bfc/wasabi_std.h>
  6. #include <tataki/bitmap/bitmap.h>
  7. #include <api/skin/skinfilter.h> // ApplySkinFilters
  8. #include "imgldr.h"
  9. #ifdef _WIN32
  10. #include <api/imgldr/winbmp.h>
  11. #endif
  12. #include <api/imgldr/skinbmps.h>
  13. #include <api/skin/skinparse.h>
  14. #include <api/skin/skinelem.h>
  15. #include <api/skin/gammamgr.h>
  16. #include <api/service/svcs/svc_skinfilter.h>
  17. #include <api/service/svcs/svc_imgload.h>
  18. #include <api/service/svcs/svc_imggen.h>
  19. #include "ImgLoaderEnum.h"
  20. #include <api/memmgr/api_memmgr.h>
  21. #include <bfc/string/PathString.h>
  22. #include <api/locales/localesmgr.h>
  23. //#define DEBUG_OUTPUT
  24. #define IMAGEHEADERLEN 256
  25. #ifdef _WIN32
  26. ARGB32 *imageLoader::makeBmp(OSMODULEHANDLE hInst, int id, int *has_alpha, int *w, int *h, const wchar_t *forcegroup)
  27. {
  28. ARGB32 *bits = makeBmp(StringPrintfW(L"res://%u,%i", hInst, id), NULL, has_alpha, w, h, NULL, TRUE, NULL);
  29. if (bits && *w > 0 && *h > 0)
  30. {
  31. ApplySkinFilters::apply(StringPrintfW(L"resource:%x,%d", hInst, id), forcegroup, bits, *w, *h);
  32. }
  33. return bits;
  34. }
  35. #endif
  36. StringW imageLoader::getWallpaper()
  37. {
  38. StringW ret(L"");
  39. #ifdef WIN32
  40. HKEY hkey;
  41. static wchar_t file[MAX_PATH];
  42. file[0] = 0;
  43. if (RegOpenKey(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), &hkey) == ERROR_SUCCESS)
  44. {
  45. unsigned long len = MAX_PATH;
  46. RegQueryValueExW(hkey, L"Wallpaper", 0, NULL, (unsigned char *)&file, &len);
  47. RegCloseKey(hkey);
  48. }
  49. if (file[0] && GetFileAttributesW(file) != (DWORD) - 1) ret = file;
  50. #endif
  51. return ret;
  52. }
  53. extern StringW g_resourcepath;
  54. ARGB32 *imageLoader::makeBmp(const wchar_t *_filename, const wchar_t *path, int *has_alpha, int *w, int *h, ifc_xmlreaderparams *params, bool addMem, int *force_nocache)
  55. {
  56. if (!_filename || !*_filename)
  57. return 0;
  58. ARGB32 *ret = NULL;
  59. if (has_alpha != NULL)
  60. *has_alpha = 0; //BU
  61. // test image generator services FIRST
  62. ImgGeneratorEnum ige(_filename);
  63. svc_imageGenerator *gen;
  64. while ((gen = ige.getNext()) != NULL)
  65. {
  66. ret = gen->genImage(_filename, has_alpha, w, h, params);
  67. int cacheable = gen->outputCacheable();
  68. ige.release(gen);
  69. if (ret != NULL)
  70. {
  71. ApplySkinFilters::apply(params->getItemValue(L"id"), params->getItemValue(L"gammagroup"), ret, *w, *h);
  72. if (addMem) addMemUsage(_filename, (*w) * (*h) * sizeof(int));
  73. optimizeHasAlpha(ret, *w * *h, has_alpha);
  74. // don't try to cache generated images
  75. if (force_nocache) *force_nocache = !cacheable;
  76. return ret;
  77. }
  78. }
  79. StringW wallpaper;
  80. if (!WCSICMP(_filename, L"$wallpaper"))
  81. {
  82. wallpaper = getWallpaper();
  83. _filename = wallpaper.getValue();
  84. }
  85. MemBlock<uint8_t> mem;
  86. wchar_t olddir[PATH_MAX] = {0};
  87. Wasabi::Std::getCurDir(olddir, PATH_MAX);
  88. Wasabi::Std::setCurDir(WASABI_API_APP->path_getAppPath());
  89. StringW file;
  90. // benski> try language pack first
  91. StringPathCombine skinLocalePath(LocalesManager::getLocaleRoot(), WASABI_API_SKIN->getSkinName());
  92. file.swap(StringPathCombine(skinLocalePath, _filename));
  93. OSFILETYPE in = WFOPEN(file, WF_READONLY_BINARY);
  94. if (in == OPEN_FAILED)
  95. {
  96. // try the language pack's folder itself before falling back to the resource path
  97. file.swap(StringPathCombine(LocalesManager::getLocaleRoot(), _filename));
  98. in = WFOPEN(file, WF_READONLY_BINARY);
  99. }
  100. if (in == OPEN_FAILED)
  101. {
  102. if (path)
  103. {
  104. file.swap(StringPathCombine(path, _filename));
  105. in = WFOPEN(file, WF_READONLY_BINARY);
  106. }
  107. else
  108. in = WFOPEN(file=_filename, WF_READONLY_BINARY);
  109. }
  110. #ifdef WASABI_COMPILE_SKIN
  111. if (in == OPEN_FAILED)
  112. {
  113. file.swap(StringPathCombine(WASABI_API_SKIN->getSkinPath(), _filename));
  114. in = WFOPEN(file, WF_READONLY_BINARY);
  115. }
  116. #if 0 // this isn't used in gen_ff, basically makes it look in C:/Program Files/Winamp/Skins/Default/
  117. if (in == OPEN_FAILED)
  118. {
  119. file.swap(StringPathCombine(Skin::getDefaultSkinPath(), _filename));
  120. in = WFOPEN(file, WF_READONLY_BINARY);
  121. }
  122. #endif
  123. // look in the fallback stuff (in Winamp5, this is c:/program files/winamp/plugins/freeform/xml)
  124. if (in == OPEN_FAILED)
  125. {
  126. file.swap(StringPathCombine(g_resourcepath, _filename));
  127. in = WFOPEN(file, WF_READONLY_BINARY);
  128. }
  129. #endif
  130. if (in == OPEN_FAILED && path)
  131. {
  132. in = WFOPEN(file = _filename, WF_READONLY_BINARY);
  133. }
  134. Wasabi::Std::setCurDir(olddir);
  135. if (in != OPEN_FAILED)
  136. {
  137. int filelen = (int)FGETSIZE(in);
  138. if (filelen > 0)
  139. {
  140. mem.setSize(filelen);
  141. int len = FREAD(mem, 1, mem.getSizeInBytes(), in);
  142. if (len == filelen)
  143. {
  144. svc_imageLoader *loader = ImgLoaderEnum(mem, len).getNext();
  145. if (loader != NULL)
  146. {
  147. ret = loader->loadImage(mem, mem.getSizeInBytes(), w, h, params);
  148. if (ret != NULL)
  149. {
  150. if (addMem)
  151. addMemUsage(file, (*w) * (*h) * sizeof(ARGB32));
  152. optimizeHasAlpha(ret, *w * *h, has_alpha);
  153. }
  154. SvcEnum::release(loader);
  155. }
  156. }
  157. } // filelen > 0
  158. FCLOSE(in);
  159. } // if file opened
  160. if (ret != NULL)
  161. {
  162. return ret;
  163. }
  164. else if (in != OPEN_FAILED && mem)
  165. {
  166. int m = getFileType(mem);
  167. #ifdef WIN32
  168. switch (m)
  169. {
  170. case FT_BMP:
  171. {
  172. wchar_t tmpname[WA_MAX_PATH] = L"";
  173. // FG> if loading bmp from disk, no need to do the copy to disk
  174. HBITMAP hbmp = (HBITMAP)LoadImageW(0, file, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
  175. if (!hbmp)
  176. {
  177. // CT> extract/copy the file into temp directory (so we don't have any trouble if the file
  178. // is in a ZIP file). this whole copying thing will go away as soon as we'll get rid of
  179. // the LoadImage win32 function and use our own bmp loading functions
  180. GetTempPathW(WA_MAX_PATH, tmpname);
  181. wcscat(tmpname, L"wa3tmp");
  182. OSFILETYPE fs = WFOPEN(file, WF_READONLY_BINARY);
  183. if (fs != OPEN_FAILED)
  184. {
  185. OSFILETYPE fd = WFOPEN(tmpname, L"wb");
  186. int l;
  187. do
  188. {
  189. char buf[1024] = {0};
  190. l = FREAD(buf, 1, sizeof(buf), fs);
  191. if (l > 0) FWRITE(buf, 1, l, fd);
  192. }
  193. while (l > 0);
  194. FCLOSE(fs);
  195. FCLOSE(fd);
  196. hbmp = (HBITMAP)LoadImageW(0, tmpname, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
  197. }
  198. if (!hbmp)
  199. {
  200. #ifdef WASABI_COMPILE_SKIN_WA2
  201. // bitmap not found or broken (like in the netscape skin)
  202. // try to find it in the Classic skin (wa2)
  203. StringW wa2skinFn = WASABI_API_APP->getSkinsPath();
  204. wa2skinFn.AppendPath("Classic");
  205. wa2skinFn.AppendPath(_filename);
  206. fs = WFOPEN(wa2skinFn), WF_READONLY_BINARY);
  207. if (fs != OPEN_FAILED)
  208. {
  209. OSFILETYPE fd = WFOPEN(tmpname, L"wb");
  210. int l;
  211. do
  212. {
  213. char buf[1024] = {0};
  214. l = FREAD(buf, 1, sizeof(buf), fs);
  215. if (l > 0) FWRITE(buf, 1, l, fd);
  216. }
  217. while (l > 0);
  218. FCLOSE(fs);
  219. FCLOSE(fd);
  220. hbmp = (HBITMAP)LoadImageW(0, tmpname, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
  221. }
  222. #endif //WASABI_COMPILE_SKIN_WA2
  223. }
  224. if (!hbmp)
  225. {
  226. // no luck :(
  227. _wunlink(tmpname);
  228. return 0;
  229. }
  230. }
  231. BITMAP bm;
  232. HDC hMemDC, hMemDC2;
  233. HBITMAP hprev, hprev2;
  234. HBITMAP hsrcdib;
  235. void *srcdib;
  236. BITMAPINFO srcbmi = {0, };
  237. int r = GetObject(hbmp, sizeof(BITMAP), &bm);
  238. ASSERT(r != 0);
  239. *w = bm.bmWidth;
  240. *h = ABS(bm.bmHeight);
  241. ARGB32 *newbits;
  242. srcbmi.bmiHeader.biSize = sizeof(srcbmi.bmiHeader);
  243. srcbmi.bmiHeader.biWidth = *w;
  244. srcbmi.bmiHeader.biHeight = -*h;
  245. srcbmi.bmiHeader.biPlanes = 1;
  246. srcbmi.bmiHeader.biBitCount = 32;
  247. srcbmi.bmiHeader.biCompression = BI_RGB;
  248. hMemDC = CreateCompatibleDC(NULL);
  249. hMemDC2 = CreateCompatibleDC(NULL);
  250. hsrcdib = CreateDIBSection(hMemDC, &srcbmi, DIB_RGB_COLORS, &srcdib, NULL, 0);
  251. ASSERTPR(hsrcdib != 0, "CreateDIBSection() failed #69");
  252. hprev2 = (HBITMAP) SelectObject(hMemDC2, hbmp);
  253. hprev = (HBITMAP) SelectObject(hMemDC, hsrcdib);
  254. BitBlt(hMemDC, 0, 0, *w, *h, hMemDC2, 0, 0, SRCCOPY);
  255. newbits = (ARGB32*)MALLOC((*w) * (*h) * sizeof(ARGB32));
  256. MEMCPY32(newbits, srcdib, (*w)*(*h) /**sizeof(ARGB32)*/);
  257. {
  258. // put the alpha channel to 255
  259. unsigned char *b = (unsigned char *)newbits;
  260. int l = (*w) * (*h);
  261. for (int i = 0;i < l;i++)
  262. b[(i*4) + 3] = 0xff;
  263. }
  264. SelectObject(hMemDC, hprev);
  265. SelectObject(hMemDC2, hprev2);
  266. DeleteObject(hsrcdib);
  267. DeleteDC(hMemDC2);
  268. DeleteDC(hMemDC);
  269. DeleteObject(hbmp);
  270. if (tmpname[0])
  271. _wunlink(tmpname); // destroy temp extraction
  272. if (addMem)
  273. addMemUsage(file, (*w)*(*h)*4);
  274. return newbits;
  275. }
  276. }
  277. #endif
  278. }
  279. return ret;
  280. }
  281. int imageLoader::getFileType(unsigned char *pData)
  282. {
  283. // Bmp ?
  284. #ifdef WIN32
  285. WINBITMAPFILEHEADER * pBFH;
  286. pBFH = (WINBITMAPFILEHEADER *) pData;
  287. #ifdef _WINDOWS
  288. if (pBFH->bfType == 0x4d42)
  289. #else
  290. if (pBFH->bfType == 0x424d)
  291. #endif
  292. return FT_BMP;
  293. #endif
  294. return FT_UNKNOWN;
  295. }
  296. #ifdef WASABI_COMPILE_SKIN
  297. ARGB32 *imageLoader::requestSkinBitmap(const wchar_t *id, int *has_alpha, int *x, int *y, int *subw, int *subh, int *w, int *h, int cached)
  298. {
  299. ifc_xmlreaderparams *params = NULL;
  300. const wchar_t *rootpath = NULL;
  301. const wchar_t *aliastarget = WASABI_API_PALETTE->getElementAlias(id);
  302. if (aliastarget)
  303. id = aliastarget;
  304. const wchar_t *efile = WASABI_API_PALETTE->getSkinBitmapFilename(id, x, y, subw, subh, &rootpath, &params);
  305. if (x && *x == -1) *x = 0;
  306. if (y && *y == -1) *y = 0;
  307. if (!efile)
  308. efile = id;
  309. if (cached)
  310. {
  311. StringPathCombine f(rootpath, efile);
  312. f.toupper();
  313. f.FixSlashes();
  314. int pos = -1;
  315. /*skinCacheEntry *entry = */skinCacheList.findItem(f.getValue(), &pos);
  316. if (pos != -1)
  317. {
  318. //find first one
  319. while (pos > 0 && !wcscmp(skinCacheList[pos - 1]->fullpathfilename, f)) pos--;
  320. do
  321. {
  322. skinCacheEntry *entry = skinCacheList[pos];
  323. if (GammaMgr::gammaEqual(entry->original_element_id, id) && layerEqual(entry->original_element_id, id))
  324. {
  325. entry->usageCount++;
  326. if (w) *w = entry->width;
  327. if (h) *h = entry->height;
  328. if (has_alpha) *has_alpha = entry->has_alpha;
  329. return entry->bitmapbits;
  330. }
  331. pos++;
  332. if (pos >= skinCacheList.getNumItems()) break;
  333. }
  334. while (!wcscmp(skinCacheList[pos]->fullpathfilename, f));
  335. }
  336. }
  337. int force_nocache = 0;
  338. int t_has_alpha = 0;
  339. ARGB32 *bits = makeBmp(efile, rootpath, &t_has_alpha, w, h, params, TRUE, &force_nocache);
  340. if (has_alpha != NULL) *has_alpha = t_has_alpha;
  341. if (!bits)
  342. return NULL;
  343. if (force_nocache || !cached) return bits;
  344. skinCacheEntry *cachedbmp = new skinCacheEntry;
  345. if (params)
  346. {
  347. for (size_t i = 0;i != params->getNbItems();i++)
  348. cachedbmp->params.addItem(params->getItemName(i), params->getItemValue(i));
  349. }
  350. cachedbmp->usageCount = 1;
  351. cachedbmp->bitmapbits = bits;
  352. cachedbmp->filename = efile;
  353. cachedbmp->has_alpha = !!t_has_alpha;
  354. cachedbmp->width = *w;
  355. cachedbmp->height = *h;
  356. cachedbmp->original_element_id = id;
  357. //needed for findItem above
  358. StringPathCombine b(rootpath, efile);
  359. b.toupper();
  360. b.FixSlashes();
  361. cachedbmp->fullpathfilename.swap(b);
  362. applySkinFilters(cachedbmp);
  363. skinCacheList.addItem(cachedbmp);
  364. return cachedbmp->bitmapbits;
  365. }
  366. /*
  367. int imageLoader::paramsMatch(ifc_xmlreaderparams *a, ifc_xmlreaderparams *b)
  368. {
  369. if (!a && !b) return 1;
  370. if ((!a && b) || (!b && a)) return 0;
  371. for (int i = 0;i < a->getNbItems();i++)
  372. {
  373. const wchar_t *name = a->getItemName(i);
  374. if (!_wcsicmp(name, L"w") || !_wcsicmp(name, L"h") || !_wcsicmp(name, L"x") || !_wcsicmp(name, L"y")) continue;
  375. if (_wcsicmp(a->getItemValue(i), b->getItemValue(name)))
  376. return 0;
  377. }
  378. return 1;
  379. }
  380. */
  381. int imageLoader::layerEqual(const wchar_t *id1, const wchar_t *id2)
  382. {
  383. int a = WASABI_API_PALETTE->getLayerFromId(id1);
  384. int b = WASABI_API_PALETTE->getLayerFromId(id2);
  385. return (a == b);
  386. }
  387. void imageLoader::releaseSkinBitmap(ARGB32 *bitmapbits)
  388. { //FG
  389. int i;
  390. // TODO: add critical sections
  391. int ni = skinCacheList.getNumItems();
  392. for (i = 0;i < ni;i++)
  393. {
  394. skinCacheEntry *entry = skinCacheList.enumItem(i);
  395. if (entry->bitmapbits == bitmapbits)
  396. {
  397. entry->usageCount--;
  398. if (entry->usageCount == 0)
  399. {
  400. subMemUsage(entry->width*entry->height*sizeof(int));
  401. WASABI_API_MEMMGR->sysFree(entry->bitmapbits);
  402. skinCacheList.removeByPos(i);
  403. delete entry;
  404. if (skinCacheList.getNumItems() == 0) skinCacheList.removeAll();
  405. }
  406. return ;
  407. }
  408. }
  409. // bitmap was not a cached skin bitmap, simply free it
  410. release(bitmapbits);
  411. }
  412. #endif //WASABI_COMPILE_SKIN
  413. void imageLoader::release(ARGB32 *bitmapbits)
  414. {
  415. WASABI_API_MEMMGR->sysFree(bitmapbits);
  416. }
  417. void imageLoader::optimizeHasAlpha(ARGB32 *bits, int len, int *has_alpha)
  418. {
  419. if (len <= 0 || has_alpha == NULL) return ;
  420. for (*has_alpha = 0; len; len--, bits++)
  421. {
  422. ARGB32 v = *bits;
  423. unsigned int alpha = v >> 24;
  424. if (alpha != 255)
  425. {
  426. *has_alpha = 1;
  427. break;
  428. }
  429. }
  430. }
  431. #ifdef WASABI_COMPILE_SKIN
  432. void imageLoader::applySkinFilters()
  433. { //FG
  434. int i;
  435. Skin::unloadResources();
  436. for (i = 0; i < skinCacheList.getNumItems(); i++)
  437. {
  438. skinCacheEntry *entry = skinCacheList.q(i);
  439. applySkinFilters(entry);
  440. }
  441. WASABI_API_PALETTE->newSkinPart();
  442. Skin::reloadResources();
  443. }
  444. void imageLoader::applySkinFilters(skinCacheEntry *entry)
  445. {
  446. ASSERT(entry != NULL);
  447. int w = entry->width, h = entry->height;
  448. ApplySkinFilters::apply(entry->original_element_id, NULL, (ARGB32*)entry->bitmapbits, w, h);
  449. }
  450. ARGB32 imageLoader::filterSkinColor(ARGB32 color, const wchar_t *element_id, const wchar_t *groupname)
  451. {
  452. SkinFilterEnum sfe;
  453. svc_skinFilter *obj;
  454. while (1)
  455. {
  456. obj = sfe.getNext(FALSE);
  457. if (!obj) break;
  458. color = obj->filterColor(color, element_id, groupname);
  459. sfe.getLastFactory()->releaseInterface(obj);
  460. }
  461. return color;
  462. }
  463. #endif //WASABI_COMPILE_SKIN
  464. void imageLoader::addMemUsage(const wchar_t *filename, int size)
  465. {
  466. totalMemUsage += size;
  467. #ifdef DEBUG_OUTPUT
  468. DebugStringW("Bitmaps memory usage : %s - %d\n", filename, totalMemUsage);
  469. #endif
  470. }
  471. void imageLoader::subMemUsage(int size)
  472. {
  473. totalMemUsage -= size;
  474. }
  475. #ifdef WASABI_COMPILE_SKIN
  476. //PtrList<skinCacheEntry> imageLoader::skinCacheList;
  477. PtrListInsertMultiSorted<skinCacheEntry, skinCacheComp> imageLoader::skinCacheList;
  478. #endif //WASABI_COMPILE_SKIN
  479. int imageLoader::totalMemUsage = 0;