PageRenderTime 77ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/MIDP/LWUIT/src/com/sun/lwuit/util/Resources.java

https://bitbucket.org/rkj/itiner-lwuit
Java | 1376 lines | 932 code | 130 blank | 314 comment | 194 complexity | a292a24b6c958846edb9b6614e5e1661 MD5 | raw file
  1. /*
  2. * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. * This code is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License version 2 only, as
  6. * published by the Free Software Foundation. Oracle designates this
  7. * particular file as subject to the "Classpath" exception as provided
  8. * by Oracle in the LICENSE file that accompanied this code.
  9. *
  10. * This code is distributed in the hope that it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  13. * version 2 for more details (a copy is included in the LICENSE file that
  14. * accompanied this code).
  15. *
  16. * You should have received a copy of the GNU General Public License version
  17. * 2 along with this work; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. * Please contact Oracle, 500 Oracle Parkway, Redwood Shores
  21. * CA 94065 USA or visit www.oracle.com if you need additional information or
  22. * have any questions.
  23. */
  24. package com.sun.lwuit.util;
  25. import com.sun.lwuit.Display;
  26. import com.sun.lwuit.EncodedImage;
  27. import com.sun.lwuit.Font;
  28. import com.sun.lwuit.Image;
  29. import com.sun.lwuit.StaticAnimation;
  30. import com.sun.lwuit.animations.AnimationObject;
  31. import com.sun.lwuit.animations.Timeline;
  32. import com.sun.lwuit.geom.Dimension;
  33. import com.sun.lwuit.plaf.Border;
  34. import com.sun.lwuit.plaf.Style;
  35. import java.io.ByteArrayInputStream;
  36. import java.io.DataInputStream;
  37. import java.io.IOException;
  38. import java.io.InputStream;
  39. import java.lang.ref.WeakReference;
  40. import java.util.Enumeration;
  41. import java.util.Hashtable;
  42. import java.util.Vector;
  43. /**
  44. * Loads resources from the binary resource file generated during the build process or via the LWUIT Designer.
  45. * A resource is loaded entirely into memory since random file access is not supported
  46. * in Java ME, any other approach would be inefficient. This means that memory must
  47. * be made available to accommodate the resource file.
  48. *
  49. * @author Shai Almog
  50. */
  51. public class Resources {
  52. /**
  53. * Magic numbers to prevent data corruption
  54. */
  55. static final byte MAGIC_THEME_LEGACY = (byte)0xF7;
  56. static final byte MAGIC_ANIMATION_LEGACY = (byte)0xF8;
  57. static final byte MAGIC_INDEXED_IMAGE_LEGACY = (byte)0xF4;
  58. static final byte MAGIC_FONT_LEGACY = (byte)0xF6;
  59. static final byte MAGIC_INDEXED_FONT_LEGACY = (byte)0xFB;
  60. static final byte MAGIC_IMAGE_LEGACY = (byte)0xF3;
  61. static final byte MAGIC_SVG = (byte)0xF5;
  62. static final byte MAGIC_TIMELINE = (byte)0xEF;
  63. static final byte MAGIC_FONT = (byte)0xFC;
  64. static final byte MAGIC_IMAGE = (byte)0xFD;
  65. static final byte MAGIC_L10N = (byte)0xF9;
  66. static final byte MAGIC_DATA = (byte)0xFA;
  67. static final byte MAGIC_UI = (byte)0xEE;
  68. static final byte MAGIC_HEADER = (byte)0xFF;
  69. private short majorVersion;
  70. private short minorVersion;
  71. /**
  72. * Temporary member for compatibility with older versions, in future versions
  73. * this will superceed the MAGIC_THEME property
  74. */
  75. static final byte MAGIC_THEME = (byte)0xF2;
  76. static final int BORDER_TYPE_EMPTY = 0;
  77. static final int BORDER_TYPE_LINE = 1;
  78. static final int BORDER_TYPE_ROUNDED = 2;
  79. static final int BORDER_TYPE_ETCHED_LOWERED = 4;
  80. static final int BORDER_TYPE_ETCHED_RAISED = 5;
  81. static final int BORDER_TYPE_BEVEL_RAISED = 6;
  82. static final int BORDER_TYPE_BEVEL_LOWERED = 7;
  83. static final int BORDER_TYPE_IMAGE = 8;
  84. static final int BORDER_TYPE_IMAGE_HORIZONTAL = 10;
  85. static final int BORDER_TYPE_IMAGE_VERTICAL = 11;
  86. // for use by the resource editor
  87. private static Class classLoader = Resources.class;
  88. private String[] metaData;
  89. /**
  90. * These variables are useful for caching the last loaded resource which might
  91. * happen frequently in the UI builder when moving between applications screens
  92. */
  93. private static WeakReference cachedResource;
  94. private static String lastLoadedName;
  95. static void setClassLoader(Class cls) {
  96. classLoader = cls;
  97. }
  98. /**
  99. * Hashtable containing the mapping between element types and their names in the
  100. * resource hashtable
  101. */
  102. private Hashtable resourceTypes = new Hashtable();
  103. /**
  104. * A cache within the resource allowing us to preserve some resources in memory
  105. * so they can be utilized by a theme when it is loaded
  106. */
  107. private Hashtable resources = new Hashtable();
  108. private DataInputStream input;
  109. // for internal use by the resource editor, creates an empty resource
  110. Resources() {
  111. }
  112. Resources(InputStream input) throws IOException {
  113. openFile(input);
  114. }
  115. void clear() {
  116. majorVersion = 0;
  117. minorVersion = 0;
  118. resourceTypes.clear();
  119. resources.clear();
  120. input = null;
  121. }
  122. /**
  123. * This method is used by the LWUIT Designer
  124. */
  125. void startingEntry(String id, byte magic) {
  126. }
  127. void openFile(InputStream input) throws IOException {
  128. clear();
  129. this.input = new DataInputStream(input);
  130. int resourceCount = this.input.readShort();
  131. if(resourceCount < 0) {
  132. throw new IOException("Invalid resource file!");
  133. }
  134. for(int iter = 0 ; iter < resourceCount ; iter++) {
  135. byte magic = this.input.readByte();
  136. String id = this.input.readUTF();
  137. startingEntry(id, magic);
  138. switch(magic) {
  139. case MAGIC_HEADER:
  140. readHeader();
  141. continue;
  142. case MAGIC_THEME:
  143. setResource(id, MAGIC_THEME, loadTheme(id, magic == MAGIC_THEME));
  144. continue;
  145. case MAGIC_IMAGE:
  146. setResource(id, magic, createImage());
  147. continue;
  148. case MAGIC_FONT:
  149. setResource(id, magic, loadFont(this.input, id, false));
  150. continue;
  151. case MAGIC_DATA:
  152. setResource(id, magic, createData());
  153. continue;
  154. case MAGIC_UI:
  155. setResource(id, magic, createData());
  156. continue;
  157. case MAGIC_L10N:
  158. setResource(id, magic, loadL10N());
  159. continue;
  160. // legacy file support to be removed
  161. case MAGIC_IMAGE_LEGACY:
  162. setResource(id, MAGIC_IMAGE, createImage());
  163. continue;
  164. case MAGIC_INDEXED_IMAGE_LEGACY:
  165. setResource(id, MAGIC_IMAGE, createPackedImage8());
  166. continue;
  167. case MAGIC_THEME_LEGACY:
  168. setResource(id, MAGIC_THEME, loadTheme(id, magic == MAGIC_THEME));
  169. continue;
  170. case MAGIC_FONT_LEGACY:
  171. setResource(id, MAGIC_FONT, loadFont(this.input, id, false));
  172. continue;
  173. case MAGIC_INDEXED_FONT_LEGACY:
  174. setResource(id, MAGIC_FONT, loadFont(this.input, id, true));
  175. continue;
  176. case MAGIC_ANIMATION_LEGACY:
  177. setResource(id, MAGIC_IMAGE, loadAnimation(this.input));
  178. continue;
  179. default:
  180. throw new IOException("Corrupt theme file unrecognized magic number: " + Integer.toHexString(magic & 0xff));
  181. }
  182. }
  183. }
  184. /**
  185. * Reads the header of the resource file
  186. */
  187. private void readHeader() throws IOException {
  188. int size = input.readShort();
  189. majorVersion = input.readShort();
  190. minorVersion = input.readShort();
  191. metaData = new String[input.readShort()];
  192. for(int iter = 0 ; iter < metaData.length ; iter++) {
  193. metaData[iter] = input.readUTF();
  194. }
  195. }
  196. /**
  197. * Returns the version number for this resource file.
  198. * This value relates to the value from the header defined by the resource file
  199. * specification. 0 is returned for legacy versions of the resource file format.
  200. *
  201. * @return major version number for the resource file
  202. */
  203. public int getMajorVersion() {
  204. return majorVersion;
  205. }
  206. /**
  207. * Returns the minor version number for this resource file
  208. * This value relates to the value from the header defined by the resource file
  209. * specification.
  210. *
  211. * @return minor version number for the resource file
  212. */
  213. public int getMinorVersion() {
  214. return minorVersion;
  215. }
  216. /**
  217. * Returns optional meta-data associated with the resource file
  218. *
  219. * @return optional meta-data associated with the file
  220. */
  221. public String[] getMetaData() {
  222. return metaData;
  223. }
  224. /**
  225. * Returns the names of the resources within this bundle
  226. *
  227. * @return array of names of all the resources in this bundle
  228. */
  229. public String[] getResourceNames() {
  230. String[] arr = new String[resourceTypes.size()];
  231. Enumeration e = resourceTypes.keys();
  232. for(int iter = 0 ; iter < arr.length ; iter++) {
  233. arr[iter] = (String)e.nextElement();
  234. }
  235. return arr;
  236. }
  237. /**
  238. * Returns the names of the data resources within this bundle
  239. *
  240. * @return array of names of the data resources in this bundle
  241. */
  242. public String[] getDataResourceNames() {
  243. return getResourceTypeNames(MAGIC_DATA);
  244. }
  245. /**
  246. * Returns the names of the ui resources within this bundle
  247. *
  248. * @return array of names of the ui resources in this bundle
  249. */
  250. public String[] getUIResourceNames() {
  251. return getResourceTypeNames(MAGIC_UI);
  252. }
  253. /**
  254. * For internal use only
  255. */
  256. void setResource(String id, byte type, Object value) {
  257. if(value == null) {
  258. resources.remove(id);
  259. resourceTypes.remove(id);
  260. } else {
  261. resources.put(id, value);
  262. resourceTypes.put(id, new Byte(type));
  263. }
  264. }
  265. /**
  266. * Returns the names of the localization bundles within this bundle
  267. *
  268. * @return array of names of the localization resources in this bundle
  269. */
  270. public String[] getL10NResourceNames() {
  271. return getResourceTypeNames(MAGIC_L10N);
  272. }
  273. /**
  274. * Returns the names of the fonts within this bundle
  275. *
  276. * @return array of names of the font resources in this bundle
  277. */
  278. public String[] getFontResourceNames() {
  279. Vector vec = new Vector();
  280. Enumeration e = resourceTypes.keys();
  281. while(e.hasMoreElements()) {
  282. String c = (String)e.nextElement();
  283. if(isFont(c)) {
  284. vec.addElement(c);
  285. }
  286. }
  287. return toStringArray(vec);
  288. }
  289. /**
  290. * Returns the names of the images within this bundle
  291. *
  292. * @return array of names of the image resources in this bundle
  293. */
  294. public String[] getThemeResourceNames() {
  295. Vector vec = new Vector();
  296. Enumeration e = resourceTypes.keys();
  297. while(e.hasMoreElements()) {
  298. String c = (String)e.nextElement();
  299. if(isTheme(c)) {
  300. vec.addElement(c);
  301. }
  302. }
  303. return toStringArray(vec);
  304. }
  305. /**
  306. * Returns the names of the images within this bundle
  307. *
  308. * @return array of names of the image resources in this bundle
  309. */
  310. public String[] getImageResourceNames() {
  311. Vector vec = new Vector();
  312. Enumeration e = resourceTypes.keys();
  313. while(e.hasMoreElements()) {
  314. String c = (String)e.nextElement();
  315. if(isImage(c)) {
  316. vec.addElement(c);
  317. }
  318. }
  319. return toStringArray(vec);
  320. }
  321. /**
  322. * Returns the names of the animations within this bundle
  323. *
  324. * @return array of names of the animation resources in this bundle
  325. * @deprecated use getImageResourceNames instead
  326. */
  327. public String[] getAnimationResourceNames() {
  328. return getResourceTypeNames(MAGIC_ANIMATION_LEGACY);
  329. }
  330. /**
  331. * For internal use only
  332. */
  333. byte getResourceType(String name) {
  334. return ((Byte)resourceTypes.get(name)).byteValue();
  335. }
  336. private String[] getResourceTypeNames(byte b) {
  337. Vector vec = new Vector();
  338. Enumeration e = resourceTypes.keys();
  339. while(e.hasMoreElements()) {
  340. String c = (String)e.nextElement();
  341. if(((Byte)resourceTypes.get(c)).byteValue() == b) {
  342. vec.addElement(c);
  343. }
  344. }
  345. return toStringArray(vec);
  346. }
  347. private static String[] toStringArray(Vector v) {
  348. String[] s = new String[v.size()];
  349. for(int iter = 0 ; iter < s.length ; iter++) {
  350. s[iter] = (String)v.elementAt(iter);
  351. }
  352. return s;
  353. }
  354. /**
  355. * Returns true if this is a generic data resource
  356. *
  357. * @param name the name of the resource
  358. * @return true if the resource is a data resource
  359. * @throws NullPointerException if the resource doesn't exist
  360. */
  361. public boolean isL10N(String name) {
  362. byte b = ((Byte)resourceTypes.get(name)).byteValue();
  363. return b == MAGIC_L10N;
  364. }
  365. /**
  366. * Returns true if this is a theme resource
  367. *
  368. * @param name the name of the resource
  369. * @return true if the resource is a theme
  370. * @throws NullPointerException if the resource doesn't exist
  371. */
  372. public boolean isTheme(String name) {
  373. byte b = ((Byte)resourceTypes.get(name)).byteValue();
  374. return b == MAGIC_THEME_LEGACY || b == MAGIC_THEME;
  375. }
  376. /**
  377. * Returns true if this is a font resource
  378. *
  379. * @param name the name of the resource
  380. * @return true if the resource is a font
  381. * @throws NullPointerException if the resource doesn't exist
  382. */
  383. public boolean isFont(String name) {
  384. byte b = ((Byte)resourceTypes.get(name)).byteValue();
  385. return b == MAGIC_FONT || b == MAGIC_FONT_LEGACY || b == MAGIC_INDEXED_FONT_LEGACY;
  386. }
  387. /**
  388. * Returns true if this is an animation resource
  389. *
  390. * @param name the name of the resource
  391. * @return true if the resource is an animation
  392. * @throws NullPointerException if the resource doesn't exist
  393. * @deprecated animations are no longer distinguished from images in the resource file, use Image.isAnimation instead
  394. */
  395. public boolean isAnimation(String name) {
  396. byte b = ((Byte)resourceTypes.get(name)).byteValue();
  397. return b == MAGIC_ANIMATION_LEGACY;
  398. }
  399. /**
  400. * Returns true if this is a data resource
  401. *
  402. * @param name the name of the resource
  403. * @return true if the resource is a data resource
  404. * @throws NullPointerException if the resource doesn't exist
  405. */
  406. public boolean isData(String name) {
  407. byte b = ((Byte)resourceTypes.get(name)).byteValue();
  408. return b == MAGIC_DATA;
  409. }
  410. /**
  411. * Returns true if this is a UI resource
  412. *
  413. * @param name the name of the resource
  414. * @return true if the resource is a UI resource
  415. * @throws NullPointerException if the resource doesn't exist
  416. */
  417. public boolean isUI(String name) {
  418. byte b = ((Byte)resourceTypes.get(name)).byteValue();
  419. return b == MAGIC_UI;
  420. }
  421. /**
  422. * Returns true if this is an image resource
  423. *
  424. * @param name the name of the resource
  425. * @return true if the resource is an image
  426. * @throws NullPointerException if the resource doesn't exist
  427. */
  428. public boolean isImage(String name) {
  429. byte b = ((Byte)resourceTypes.get(name)).byteValue();
  430. return b == MAGIC_IMAGE_LEGACY || b == MAGIC_ANIMATION_LEGACY ||
  431. b == MAGIC_INDEXED_IMAGE_LEGACY || b == MAGIC_IMAGE ||
  432. b == MAGIC_TIMELINE;
  433. }
  434. /**
  435. * Creates a resource object from the local JAR resource identifier
  436. *
  437. * @param resource a local reference to a resource using the syntax of Class.getResourceAsStream(String)
  438. * @return a resource object
  439. * @throws java.io.IOException if opening/reading the resource fails
  440. */
  441. public static Resources open(String resource) throws IOException {
  442. try {
  443. if(lastLoadedName != null && lastLoadedName.equals(resource)) {
  444. Resources r = (Resources)cachedResource.get();
  445. if(r != null) {
  446. return r;
  447. }
  448. }
  449. InputStream is = Display.getInstance().getResourceAsStream(classLoader, resource);
  450. if(is == null) {
  451. throw new IOException(resource + " not found");
  452. }
  453. Resources r = new Resources(is);
  454. is.close();
  455. lastLoadedName = resource;
  456. cachedResource = new WeakReference(r);
  457. return r;
  458. } catch(RuntimeException err) {
  459. // intercept exceptions since user code might not deal well with runtime exceptions
  460. err.printStackTrace();
  461. throw new IOException(err.getMessage());
  462. }
  463. }
  464. StaticAnimation loadAnimation(DataInputStream input) throws IOException {
  465. return StaticAnimation.createAnimation(input);
  466. }
  467. /**
  468. * Creates a resource object from the given input stream
  469. *
  470. * @param resource stream from which to read the resource
  471. * @return a resource object
  472. * @throws java.io.IOException if opening/reading the resource fails
  473. */
  474. public static Resources open(InputStream resource) throws IOException {
  475. return new Resources(resource);
  476. }
  477. /**
  478. * Returns the image resource from the file
  479. *
  480. * @param id name of the image resource
  481. * @return cached image instance
  482. */
  483. public Image getImage(String id) {
  484. return (Image)resources.get(id);
  485. }
  486. /**
  487. * Returns the animation resource from the file
  488. *
  489. * @param id name of the animation resource
  490. * @return cached image instance
  491. * @deprecated use getImage(String) instead
  492. */
  493. public StaticAnimation getAnimation(String id) {
  494. return (StaticAnimation)resources.get(id);
  495. }
  496. /**
  497. * Returns the data resource from the file
  498. *
  499. * @param id name of the data resource
  500. * @return newly created input stream that allows reading the data of the resource
  501. */
  502. public InputStream getData(String id) {
  503. return new ByteArrayInputStream((byte[])resources.get(id));
  504. }
  505. /**
  506. * Returns the ui resource from the file
  507. *
  508. * @param id name of the ui resource
  509. * @return newly created input stream that allows reading the ui of the resource
  510. */
  511. InputStream getUi(String id) {
  512. return new ByteArrayInputStream((byte[])resources.get(id));
  513. }
  514. /**
  515. * Returns a hashmap containing localized String key/value pairs for the given locale name
  516. *
  517. * @param id the name of the locale resource
  518. * @param locale name of the locale resource
  519. * @return Hashtable containing key value pairs for localized data
  520. */
  521. public Hashtable getL10N(String id, String locale) {
  522. return (Hashtable)((Hashtable)resources.get(id)).get(locale);
  523. }
  524. /**
  525. * Returns an enumration of the locales supported by this resource id
  526. *
  527. * @param id the name of the locale resource
  528. * @return enumeration of strings containing bundle names
  529. */
  530. public Enumeration listL10NLocales(String id) {
  531. return ((Hashtable)resources.get(id)).keys();
  532. }
  533. /**
  534. * Returns the font resource from the file
  535. *
  536. * @param id name of the font resource
  537. * @return cached font instance
  538. */
  539. public Font getFont(String id) {
  540. return (Font)resources.get(id);
  541. }
  542. /**
  543. * Returns the theme resource from the file
  544. *
  545. * @param id name of the theme resource
  546. * @return cached theme instance
  547. */
  548. public Hashtable getTheme(String id) {
  549. Hashtable h = (Hashtable)resources.get(id);
  550. // theme can be null in valid use cases such as the resource editor
  551. if(h != null && h.containsKey("uninitialized")) {
  552. Enumeration e = h.keys();
  553. while(e.hasMoreElements()) {
  554. String key = (String)e.nextElement();
  555. if(key.endsWith("font") || (key.endsWith("Image") && !key.endsWith("scaledImage"))) {
  556. Object value = h.get(key);
  557. if(value == null) {
  558. throw new IllegalArgumentException("Couldn't find resource: " + key);
  559. }
  560. // the resource was not already loaded when we loaded the theme
  561. // it must be loaded now so we can resolve the temporary name
  562. if(value instanceof String) {
  563. Object o = resources.get(value);
  564. if(o == null) {
  565. throw new IllegalArgumentException("Theme entry for " + key + " could not be found: " + value);
  566. }
  567. h.put(key, o);
  568. }
  569. }
  570. // if this is a border we might need to do additional work for older versions
  571. // of LWUIT and for the case of an image border where the images might not have
  572. // been loaded yet when the border was created
  573. if(key.endsWith("order")) {
  574. Border b = confirmBorder(h, key);
  575. if(majorVersion == 0 && minorVersion == 0) {
  576. b.setPressedInstance(confirmBorder(h, key + "Pressed"));
  577. b.setFocusedInstance(confirmBorder(h, key + "Focused"));
  578. h.remove(key + "Pressed");
  579. h.remove(key + "Focused");
  580. }
  581. h.put(key, b);
  582. }
  583. }
  584. h.remove("uninitialized");
  585. }
  586. return h;
  587. }
  588. private Border confirmBorder(Hashtable h, String key) {
  589. Object val = h.get(key);
  590. if(val == null) {
  591. return null;
  592. }
  593. if(!(val instanceof Border)) {
  594. String[] value = (String[])val;
  595. if(value == null) {
  596. throw new IllegalArgumentException("Couldn't find resource: " + key);
  597. }
  598. // the resource was not already loaded when we loaded the theme
  599. // it must be loaded now so we can resolve the temporary name
  600. Border imageBorder = createImageBorder(value);
  601. return imageBorder;
  602. }
  603. return (Border)val;
  604. }
  605. private Border createImageBorder(String[] value) {
  606. if(value[0].equals("h")) {
  607. return Border.createHorizonalImageBorder(getImage(value[1]),
  608. getImage(value[2]), getImage(value[3]));
  609. }
  610. if(value[0].equals("v")) {
  611. return Border.createVerticalImageBorder(getImage(value[1]),
  612. getImage(value[2]), getImage(value[3]));
  613. }
  614. Image[] images = new Image[value.length];
  615. for(int iter = 0 ; iter < value.length ; iter++) {
  616. images[iter] = getImage(value[iter]);
  617. }
  618. switch(images.length) {
  619. case 2:
  620. return Border.createImageBorder(images[0], images[1], null);
  621. case 3:
  622. return Border.createImageBorder(images[0], images[1], images[2]);
  623. case 8:
  624. return Border.createImageBorder(images[0], images[1], images[2],
  625. images[3], images[4], images[5], images[6], images[7], null);
  626. default:
  627. return Border.createImageBorder(images[0], images[1], images[2],
  628. images[3], images[4], images[5], images[6], images[7], images[8]);
  629. }
  630. }
  631. Object getResourceObject(String res) {
  632. return resources.get(res);
  633. }
  634. Image createImage() throws IOException {
  635. if(majorVersion == 0 && minorVersion == 0) {
  636. byte[] data = new byte[input.readInt()];
  637. input.readFully(data, 0, data.length);
  638. return EncodedImage.create(data);
  639. } else {
  640. int type = input.readByte() & 0xff;
  641. switch(type) {
  642. // PNG file
  643. case 0xf1:
  644. // JPEG File
  645. case 0xf2:
  646. byte[] data = new byte[input.readInt()];
  647. input.readFully(data, 0, data.length);
  648. return EncodedImage.create(data);
  649. // Indexed image
  650. case 0xF3:
  651. return createPackedImage8();
  652. // animation
  653. case 0xF4:
  654. return loadAnimation(input);
  655. // SVG
  656. case 0xF5: {
  657. int svgSize = input.readInt();
  658. if(Image.isSVGSupported()) {
  659. byte[] s = new byte[svgSize];
  660. input.readFully(s);
  661. String baseURL = input.readUTF();
  662. boolean animated = input.readBoolean();
  663. loadSVGRatios(input);
  664. byte[] fallback = new byte[input.readInt()];
  665. if(fallback.length > 0) {
  666. input.readFully(fallback, 0, fallback.length);
  667. }
  668. return Image.createSVG(baseURL, animated, s);
  669. } else {
  670. svgSize -= input.skip(svgSize);
  671. while(svgSize > 0) {
  672. svgSize -= input.skip(svgSize);
  673. }
  674. // read the base url, the animated property and screen ratios to skip them as well...
  675. input.readUTF();
  676. input.readBoolean();
  677. input.readFloat();
  678. input.readFloat();
  679. byte[] fallback = new byte[input.readInt()];
  680. input.readFully(fallback, 0, fallback.length);
  681. return EncodedImage.create(fallback);
  682. }
  683. }
  684. // SVG with multi-image
  685. case 0xf7: {
  686. int svgSize = input.readInt();
  687. if(Image.isSVGSupported()) {
  688. byte[] s = new byte[svgSize];
  689. input.readFully(s);
  690. boolean animated = input.readBoolean();
  691. readMultiImage(input, true);
  692. return createSVG(animated, s);
  693. } else {
  694. svgSize -= input.skip(svgSize);
  695. while(svgSize > 0) {
  696. svgSize -= input.skip(svgSize);
  697. }
  698. // read the animated property to skip it as well...
  699. input.readBoolean();
  700. return readMultiImage(input);
  701. }
  702. }
  703. // mutli image
  704. case 0xF6:
  705. return readMultiImage(input);
  706. case 0xEF:
  707. int duration = input.readInt();
  708. int width = input.readInt();
  709. int height = input.readInt();
  710. AnimationObject[] animations = new AnimationObject[input.readShort()];
  711. for(int iter = 0 ; iter < animations.length ; iter++) {
  712. String name = input.readUTF();
  713. int startTime = input.readInt();
  714. int animDuration = input.readInt();
  715. int x = input.readInt();
  716. int y = input.readInt();
  717. Image i = getImage(name);
  718. if(i == null) {
  719. animations[iter] = AnimationObject.createAnimationImage(name, this, x, y);
  720. } else {
  721. animations[iter] = AnimationObject.createAnimationImage(i, x, y);
  722. }
  723. animations[iter].setStartTime(startTime);
  724. animations[iter].setEndTime(startTime + animDuration);
  725. int frameDelay = input.readInt();
  726. if(frameDelay > -1) {
  727. int frameWidth = input.readInt();
  728. int frameHeight = input.readInt();
  729. animations[iter].defineFrames(frameWidth, frameHeight, frameDelay);
  730. }
  731. if(input.readBoolean()) {
  732. animations[iter].defineMotionX(input.readInt(), startTime, animDuration, x, input.readInt());
  733. }
  734. if(input.readBoolean()) {
  735. animations[iter].defineMotionY(input.readInt(), startTime, animDuration, y, input.readInt());
  736. }
  737. if(input.readBoolean()) {
  738. animations[iter].defineWidth(input.readInt(), startTime, animDuration, input.readInt(), input.readInt());
  739. }
  740. if(input.readBoolean()) {
  741. animations[iter].defineHeight(input.readInt(), startTime, animDuration, input.readInt(), input.readInt());
  742. }
  743. if(input.readBoolean()) {
  744. animations[iter].defineOpacity(input.readInt(), startTime, animDuration, input.readInt(), input.readInt());
  745. }
  746. if(input.readBoolean()) {
  747. animations[iter].defineOrientation(input.readInt(), startTime, animDuration, input.readInt(), input.readInt());
  748. }
  749. }
  750. Timeline tl = Timeline.createTimeline(duration, animations, new Dimension(width, height));
  751. return tl;
  752. // Fail this is the wrong data type
  753. default:
  754. throw new IOException("Illegal type while creating image: " + Integer.toHexString(type));
  755. }
  756. }
  757. }
  758. com.sun.lwuit.Image createSVG(boolean animated, byte[] data) throws IOException {
  759. return Image.createSVG(null, animated, data);
  760. }
  761. private Image readMultiImage(DataInputStream input) throws IOException {
  762. return readMultiImage(input, false);
  763. }
  764. Image readMultiImage(DataInputStream input, boolean skipAll) throws IOException {
  765. EncodedImage resultImage = null;
  766. int dpi = Display.getInstance().getDeviceDensity();
  767. int dpiCount = input.readInt();
  768. int bestFitOffset = 0;
  769. int bestFitDPI = 0;
  770. int[] lengths = new int[dpiCount];
  771. for(int iter = 0 ; iter < dpiCount ; iter++) {
  772. int currentDPI = input.readInt();
  773. lengths[iter] = input.readInt();
  774. if(bestFitDPI != dpi && dpi >= currentDPI && currentDPI >= bestFitDPI) {
  775. bestFitDPI = currentDPI;
  776. bestFitOffset = iter;
  777. }
  778. }
  779. for(int iter = 0 ; iter < lengths.length ; iter++) {
  780. int size = lengths[iter];
  781. if(!skipAll && bestFitOffset == iter) {
  782. byte[] multiImageData = new byte[size];
  783. input.readFully(multiImageData, 0, size);
  784. resultImage = EncodedImage.create(multiImageData);
  785. } else {
  786. while(size > 0) {
  787. size -= input.skip(size);
  788. }
  789. }
  790. }
  791. return resultImage;
  792. }
  793. void loadSVGRatios(DataInputStream input) throws IOException {
  794. input.readFloat();
  795. input.readFloat();
  796. }
  797. private byte[] createData() throws IOException {
  798. byte[] data = new byte[input.readInt()];
  799. input.readFully(data);
  800. return data;
  801. }
  802. Font loadFont(DataInputStream input, String id, boolean packed) throws IOException {
  803. if(majorVersion == 0 && minorVersion == 0) {
  804. Image bitmap;
  805. if(packed) {
  806. bitmap = createPackedImage8();
  807. } else {
  808. bitmap = createImage();
  809. }
  810. int charCount = input.readShort();
  811. int[] cutOffsets = new int[charCount];
  812. int[] charWidth = new int[charCount];
  813. for(int iter = 0 ; iter < charCount ; iter++) {
  814. cutOffsets[iter] = input.readShort();
  815. charWidth[iter] = input.readByte();
  816. }
  817. String charset = input.readUTF();
  818. Font old = Font.getBitmapFont(id);
  819. if(old != null) {
  820. return old;
  821. }
  822. return Font.createBitmapFont(id, bitmap, cutOffsets, charWidth, charset);
  823. }
  824. // read a system font fallback
  825. int fallback = input.readByte() & 0xff;
  826. // do we have an emedded truetype font? Do we support embedded fonts?
  827. boolean trueTypeIncluded = input.readBoolean();
  828. Font font = null;
  829. if(trueTypeIncluded) {
  830. int size = input.readInt();
  831. if(Font.isTrueTypeFileSupported()) {
  832. font = Font.createTrueTypeFont(input);
  833. } else {
  834. while(size > 0) {
  835. size -= input.skip(size);
  836. }
  837. }
  838. }
  839. boolean lookupIncluded = input.readBoolean();
  840. if(lookupIncluded) {
  841. String lookup = input.readUTF();
  842. if(font == null && Font.isCreationByStringSupported()) {
  843. font = Font.create(lookup);
  844. }
  845. }
  846. boolean bitmapIncluded = input.readBoolean();
  847. if(bitmapIncluded) {
  848. font = loadBitmapFont(input, id, font);
  849. }
  850. if(font != null) {
  851. return font;
  852. }
  853. return Font.createSystemFont(fallback & (Font.FACE_MONOSPACE | Font.FACE_PROPORTIONAL | Font.FACE_SYSTEM),
  854. fallback & (Font.STYLE_BOLD | Font.STYLE_ITALIC | Font.STYLE_PLAIN | Font.STYLE_UNDERLINED),
  855. fallback & (Font.SIZE_LARGE | Font.SIZE_MEDIUM| Font.SIZE_SMALL));
  856. }
  857. void readRenderingHint(DataInputStream i) throws IOException {
  858. i.readByte();
  859. }
  860. Font loadBitmapFont(DataInputStream input, String id, com.sun.lwuit.Font font) throws IOException {
  861. Image bitmap = createImage();
  862. int charCount = input.readShort();
  863. int[] cutOffsets = new int[charCount];
  864. int[] charWidth = new int[charCount];
  865. for(int iter = 0 ; iter < charCount ; iter++) {
  866. cutOffsets[iter] = input.readShort();
  867. }
  868. for(int iter = 0 ; iter < charCount ; iter++) {
  869. charWidth[iter] = input.readByte();
  870. }
  871. String charset = input.readUTF();
  872. readRenderingHint(input);
  873. if(font == null) {
  874. if(Font.isBitmapFontEnabled()) {
  875. Font old = Font.getBitmapFont(id);
  876. if(old != null) {
  877. // Returning bitmap font from cache, to prevent collision with an
  878. // old resource file use Font.clearBitmapCache()
  879. return old;
  880. }
  881. return Font.createBitmapFont(id, bitmap, cutOffsets, charWidth, charset);
  882. }
  883. }
  884. return font;
  885. }
  886. Hashtable loadTheme(String id, boolean newerVersion) throws IOException {
  887. Hashtable theme = new Hashtable();
  888. theme.put("name", id);
  889. // marks the theme as uninitialized so we can finish "wiring" cached resources
  890. theme.put("uninitialized", Boolean.TRUE);
  891. int size = input.readShort();
  892. for(int iter = 0 ; iter < size ; iter++) {
  893. String key = input.readUTF();
  894. if(key.startsWith("@")) {
  895. theme.put(key, input.readUTF());
  896. continue;
  897. }
  898. // if this is a simple numeric value
  899. if(key.endsWith("Color")) {
  900. theme.put(key, Integer.toHexString(input.readInt()));
  901. continue;
  902. }
  903. if(key.endsWith("align") || key.endsWith("textDecoration")) {
  904. theme.put(key, new Integer(input.readShort()));
  905. continue;
  906. }
  907. // if this is a short numeric value for transparency
  908. if(key.endsWith("ransparency")) {
  909. theme.put(key, "" + (input.readByte() & 0xff));
  910. continue;
  911. }
  912. // if this is a padding or margin then we will have the 4 values as bytes
  913. if(key.endsWith("adding") || key.endsWith("argin")) {
  914. int p1 = input.readByte() & 0xff;
  915. int p2 = input.readByte() & 0xff;
  916. int p3 = input.readByte() & 0xff;
  917. int p4 = input.readByte() & 0xff;
  918. theme.put(key, "" + p1 + "," + p2 + "," + p3 + "," + p4);
  919. continue;
  920. }
  921. // border
  922. if(key.endsWith("order")) {
  923. if(majorVersion == 0 && minorVersion == 0) {
  924. theme.put(key, createBorder(input, newerVersion));
  925. if(newerVersion) {
  926. if(input.readBoolean()) {
  927. theme.put(key + "Pressed", createBorder(input, true));
  928. }
  929. if(input.readBoolean()) {
  930. theme.put(key + "Focused", createBorder(input, true));
  931. }
  932. }
  933. } else {
  934. int borderType = input.readShort() & 0xffff;
  935. Object b = createBorder(input, borderType);
  936. theme.put(key, b);
  937. }
  938. continue;
  939. }
  940. // if this is a font
  941. if(key.endsWith("ont")) {
  942. Font f;
  943. // is this a new font?
  944. if(input.readBoolean()) {
  945. String fontId = input.readUTF();
  946. f = (Font)resources.get(fontId);
  947. // if the font is not yet loaded
  948. if(f == null) {
  949. theme.put(key, fontId);
  950. continue;
  951. }
  952. } else {
  953. f = Font.createSystemFont(input.readByte(), input.readByte(), input.readByte());
  954. }
  955. theme.put(key, f);
  956. continue;
  957. }
  958. // the background property
  959. if(key.endsWith("ackground")) {
  960. int type = input.readByte() & 0xff;
  961. int pos = key.indexOf('.');
  962. if(pos > -1) {
  963. key = key.substring(0, pos);
  964. } else {
  965. key = "";
  966. }
  967. theme.put(key + Style.BACKGROUND_TYPE, new Byte((byte)type));
  968. switch(type) {
  969. // Scaled Image
  970. case 0xF1:
  971. // Tiled Both Image
  972. case MAGIC_INDEXED_IMAGE_LEGACY:
  973. // the image name coupled with the type
  974. theme.put(key + Style.BG_IMAGE, input.readUTF());
  975. break;
  976. // Aligned Image
  977. case 0xf5:
  978. // Tiled Vertically Image
  979. case 0xF2:
  980. // Tiled Horizontally Image
  981. case 0xF3:
  982. // the image name coupled with the type and with alignment information
  983. String imageName = input.readUTF();
  984. theme.put(key + Style.BG_IMAGE, imageName);
  985. byte align = input.readByte();
  986. theme.put(key + Style.BACKGROUND_ALIGNMENT, new Byte(align));
  987. break;
  988. // Horizontal Linear Gradient
  989. case 0xF6:
  990. // Vertical Linear Gradient
  991. case 0xF7:
  992. Float c = new Float(0.5f);
  993. theme.put(key + Style.BACKGROUND_GRADIENT, new Object[] {new Integer(input.readInt()), new Integer(input.readInt()),c, c, new Float(1)});
  994. break;
  995. // Radial Gradient
  996. case 0xF8:
  997. int c1 = input.readInt();
  998. int c2 = input.readInt();
  999. float f1 = input.readFloat();
  1000. float f2 = input.readFloat();
  1001. float radialSize = 1;
  1002. if(minorVersion > 1) {
  1003. radialSize = input.readFloat();
  1004. }
  1005. theme.put(key + Style.BACKGROUND_GRADIENT, new Object[] {new Integer(c1),
  1006. new Integer(c2),
  1007. new Float(f1),
  1008. new Float(f2),
  1009. new Float(radialSize)});
  1010. break;
  1011. }
  1012. continue;
  1013. }
  1014. // if this is a background image bgImage
  1015. if(key.endsWith("derive")) {
  1016. theme.put(key, input.readUTF());
  1017. continue;
  1018. }
  1019. // if this is a background image bgImage
  1020. if(key.endsWith("bgImage")) {
  1021. String imageId = input.readUTF();
  1022. Image i = (Image)resources.get(imageId);
  1023. // if the font is not yet loaded
  1024. if(i == null) {
  1025. theme.put(key, imageId);
  1026. continue;
  1027. }
  1028. theme.put(key, i);
  1029. continue;
  1030. }
  1031. if(key.endsWith("scaledImage")) {
  1032. if(input.readBoolean()) {
  1033. theme.put(key, "true");
  1034. } else {
  1035. theme.put(key, "false");
  1036. }
  1037. continue;
  1038. }
  1039. if(key.endsWith(Style.BACKGROUND_TYPE) || key.endsWith(Style.BACKGROUND_ALIGNMENT)) {
  1040. theme.put(key, new Byte(input.readByte()));
  1041. continue;
  1042. }
  1043. if(key.endsWith(Style.BACKGROUND_GRADIENT)) {
  1044. if(minorVersion < 2) {
  1045. theme.put(key, new Object[] {
  1046. new Integer(input.readInt()),
  1047. new Integer(input.readInt()),
  1048. new Float(input.readFloat()),
  1049. new Float(input.readFloat())
  1050. });
  1051. } else {
  1052. theme.put(key, new Object[] {
  1053. new Integer(input.readInt()),
  1054. new Integer(input.readInt()),
  1055. new Float(input.readFloat()),
  1056. new Float(input.readFloat()),
  1057. new Float(input.readFloat())
  1058. });
  1059. }
  1060. continue;
  1061. }
  1062. // thow an exception no idea what this is
  1063. throw new IOException("Error while trying to read theme property: " + key);
  1064. }
  1065. return theme;
  1066. }
  1067. private Object createBorder(DataInputStream input, int type) throws IOException {
  1068. switch(type) {
  1069. // empty border
  1070. case 0xff01:
  1071. return Border.getEmpty();
  1072. // Line border
  1073. case 0xff02:
  1074. // use theme colors?
  1075. if(input.readBoolean()) {
  1076. return Border.createLineBorder(input.readByte());
  1077. } else {
  1078. return Border.createLineBorder(input.readByte(), input.readInt());
  1079. }
  1080. // Rounded border
  1081. case 0xff03:
  1082. // use theme colors?
  1083. if(input.readBoolean()) {
  1084. return Border.createRoundBorder(input.readByte(), input.readByte());
  1085. } else {
  1086. return Border.createRoundBorder(input.readByte(), input.readByte(), input.readInt());
  1087. }
  1088. // Etched Lowered border
  1089. case 0xff04:
  1090. // use theme colors?
  1091. if(input.readBoolean()) {
  1092. return Border.createEtchedLowered();
  1093. } else {
  1094. return Border.createEtchedLowered(input.readInt(), input.readInt());
  1095. }
  1096. // Etched raised border
  1097. case 0xff05:
  1098. // use theme colors?
  1099. if(input.readBoolean()) {
  1100. return Border.createEtchedRaised();
  1101. } else {
  1102. return Border.createEtchedRaised(input.readInt(), input.readInt());
  1103. }
  1104. // Bevel raised
  1105. case 0xff07:
  1106. // use theme colors?
  1107. if(input.readBoolean()) {
  1108. return Border.createBevelRaised();
  1109. } else {
  1110. return Border.createBevelRaised(input.readInt(), input.readInt(), input.readInt(), input.readInt());
  1111. }
  1112. // Bevel lowered
  1113. case 0xff06:
  1114. // use theme colors?
  1115. if(input.readBoolean()) {
  1116. return Border.createBevelLowered();
  1117. } else {
  1118. return Border.createBevelLowered(input.readInt(), input.readInt(), input.readInt(), input.readInt());
  1119. }
  1120. // Image border
  1121. case 0xff08:
  1122. Object[] imageBorder = readImageBorder(input);
  1123. return imageBorder;
  1124. // horizontal Image border
  1125. case 0xff09:
  1126. return readImageBorder(input, "h");
  1127. // vertical Image border
  1128. case 0xff10:
  1129. return readImageBorder(input, "v");
  1130. }
  1131. return null;
  1132. }
  1133. private Object createBorder(DataInputStream input, boolean newerVersion) throws IOException {
  1134. int type = input.readByte();
  1135. switch(type) {
  1136. case BORDER_TYPE_EMPTY:
  1137. return Border.getEmpty();
  1138. case BORDER_TYPE_LINE:
  1139. // use theme colors?
  1140. if(input.readBoolean()) {
  1141. return Border.createLineBorder(input.readByte());
  1142. } else {
  1143. return Border.createLineBorder(input.readByte(), input.readInt());
  1144. }
  1145. case BORDER_TYPE_ROUNDED:
  1146. // use theme colors?
  1147. if(input.readBoolean()) {
  1148. return Border.createRoundBorder(input.readByte(), input.readByte());
  1149. } else {
  1150. return Border.createRoundBorder(input.readByte(), input.readByte(), input.readInt());
  1151. }
  1152. case BORDER_TYPE_ETCHED_LOWERED:
  1153. // use theme colors?
  1154. if(input.readBoolean()) {
  1155. return Border.createEtchedLowered();
  1156. } else {
  1157. return Border.createEtchedLowered(input.readInt(), input.readInt());
  1158. }
  1159. case BORDER_TYPE_ETCHED_RAISED:
  1160. // use theme colors?
  1161. if(input.readBoolean()) {
  1162. return Border.createEtchedRaised();
  1163. } else {
  1164. return Border.createEtchedRaised(input.readInt(), input.readInt());
  1165. }
  1166. case BORDER_TYPE_BEVEL_RAISED:
  1167. // use theme colors?
  1168. if(input.readBoolean()) {
  1169. return Border.createBevelRaised();
  1170. } else {
  1171. return Border.createBevelRaised(input.readInt(), input.readInt(), input.readInt(), input.readInt());
  1172. }
  1173. case BORDER_TYPE_BEVEL_LOWERED:
  1174. // use theme colors?
  1175. if(input.readBoolean()) {
  1176. return Border.createBevelLowered();
  1177. } else {
  1178. return Border.createBevelLowered(input.readInt(), input.readInt(), input.readInt(), input.readInt());
  1179. }
  1180. case BORDER_TYPE_IMAGE:
  1181. Object[] imageBorder = readImageBorder(input);
  1182. if(!newerVersion) {
  1183. // legacy issue...
  1184. input.readBoolean();
  1185. }
  1186. return imageBorder;
  1187. }
  1188. return null;
  1189. }
  1190. private String[] readImageBorder(DataInputStream input, String orientation) throws IOException {
  1191. String[] imageBorder = new String[4];
  1192. imageBorder[0] = orientation;
  1193. for(int iter = 1 ; iter < 4 ; iter++) {
  1194. imageBorder[iter] = input.readUTF();
  1195. }
  1196. return imageBorder;
  1197. }
  1198. private String[] readImageBorder(DataInputStream input) throws IOException {
  1199. // Read number of images can be 2, 3, 8 or 9
  1200. int size = input.readByte();
  1201. String[] imageBorder = new String[size];
  1202. for(int iter = 0 ; iter < size ; iter++) {
  1203. imageBorder[iter] = input.readUTF();
  1204. }
  1205. return imageBorder;
  1206. }
  1207. private Hashtable loadL10N() throws IOException {
  1208. Hashtable l10n = new Hashtable();
  1209. int keys = input.readShort();
  1210. int languages = input.readShort();
  1211. String[] keyArray = new String[keys];
  1212. for(int iter = 0 ; iter < keys ; iter++) {
  1213. String key = input.readUTF();
  1214. keyArray[iter] = key;
  1215. }
  1216. for(int iter = 0 ; iter < languages ; iter++) {
  1217. Hashtable currentLanguage = new Hashtable();
  1218. String lang = input.readUTF();
  1219. l10n.put(lang, currentLanguage);
  1220. for(int valueIter = 0 ; valueIter < keys ; valueIter++) {
  1221. currentLanguage.put(keyArray[valueIter], input.readUTF());
  1222. }
  1223. }
  1224. return l10n;
  1225. }
  1226. /**
  1227. * Creates a packed image from the input stream for an 8 bit packed image
  1228. */
  1229. private Image createPackedImage8() throws IOException {
  1230. // read the length of the palette;
  1231. int size = input.readByte() & 0xff;
  1232. // 0 means the last bit overflowed, there is no sense for 0 sized palette
  1233. if(size == 0) {
  1234. size = 256;
  1235. }
  1236. int[] palette = new int[size];
  1237. for(int iter = 0 ; iter < palette.length ; iter++) {
  1238. palette[iter] = input.readInt();
  1239. }
  1240. int width = input.readShort();
  1241. int height = input.readShort();
  1242. byte[] data = new byte[width * height];
  1243. input.readFully(data, 0, data.length);
  1244. return Image.createIndexed(width, height, palette, data);
  1245. }
  1246. }