public synchronized ImageResource get(final String uri, final int width, final int height) {
    if (ImageUtil.isEmbeddedBase64Image(uri)) {
      ImageResource resource = loadEmbeddedBase64ImageResource(uri);
      BufferedImage newImg = ((AWTFSImage) resource.getImage()).getImage();
      return new ImageResource(
          resource.getImageUri(),
          AWTFSImage.createImage(ImageUtils.scaleImage(newImg, width, height)));
    } else {
      CacheKey key = new CacheKey(uri, width, height);
      ImageResource ir = (ImageResource) _imageCache.get(key);
      if (ir == null) {
        // not loaded, or not loaded at target size

        // loaded a base size?
        ir = (ImageResource) _imageCache.get(new CacheKey(uri, -1, -1));

        // no: loaded
        if (ir == null) {
          if (isImmediateLoadUri(uri)) {
            XRLog.load(Level.FINE, "Load immediate: " + uri);
            ir = loadImageResourceFromUri(uri);
            FSImage awtfsImage = ir.getImage();
            BufferedImage newImg = ((AWTFSImage) awtfsImage).getImage();
            loaded(ir, -1, -1);
            if (width > -1 && height > -1) {
              XRLog.load(Level.FINE, this + ", scaling " + uri + " to " + width + ", " + height);
              newImg = ImageUtils.scaleImage(newImg, width, height);
              ir = new ImageResource(ir.getImageUri(), AWTFSImage.createImage(newImg));
              loaded(ir, width, height);
            }
          } else {
            XRLog.load(Level.FINE, "Image cache miss, URI not yet loaded, queueing: " + uri);
            MutableFSImage mfsi = new MutableFSImage(_repaintListener);
            ir = new ImageResource(uri, mfsi);
            _loadQueue.addToQueue(this, uri, mfsi, width, height);
          }

          _imageCache.put(key, ir);
        } else {
          // loaded at base size, need to scale
          XRLog.load(Level.FINE, this + ", scaling " + uri + " to " + width + ", " + height);
          FSImage awtfsImage = ir.getImage();
          BufferedImage newImg = ((AWTFSImage) awtfsImage).getImage();

          newImg = ImageUtils.scaleImage(newImg, width, height);
          ir = new ImageResource(ir.getImageUri(), AWTFSImage.createImage(newImg));
          loaded(ir, width, height);
        }
      }
      return ir;
    }
  }
  /**
   * The image we're replacing.
   *
   * @return see desc
   */
  public Image getImage() {
    if (!_loaded && _imageResource.isLoaded()) {
      Image image = ((AWTFSImage) _imageResource.getImage()).getImage();
      if (_doScaleImage && (_targetWidth > 0 || _targetHeight > 0)) {
        int w = image.getWidth(null);
        int h = image.getHeight(null);
        int newW = _targetWidth;
        int newH = _targetHeight;

        if (newW == -1) {
          newW = (int) (w * ((double) newH / h));
        }

        if (newH == -1) {
          newH = (int) (h * ((double) newW / w));
        }

        if (w != newW || h != newH) {
          if (image instanceof BufferedImage) {
            image = ImageUtil.getScaledInstance((BufferedImage) image, newW, newH);
          } else {
            if (true) {
              throw new RuntimeException(
                  "image is not a buffered image! " + _imageResource.getImageUri());
            }
            String scalingType = Configuration.valueFor("xr.image.scale", "HIGH").trim();

            if (scalingType.equalsIgnoreCase("HIGH") || scalingType.equalsIgnoreCase("MID")) {
              image = image.getScaledInstance(newW, newH, Image.SCALE_SMOOTH);
            } else {
              image = image.getScaledInstance(newW, newH, Image.SCALE_FAST);
            }
          }
        }
        _image = image;
      } else {
        _image = image;
      }
      _loaded = true;
      XRLog.load(
          Level.FINE,
          "Icon: replaced image " + _imageResource.getImageUri() + ", repaint requested");
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              repaintListener.repaintRequested(_doScaleImage);
            }
          });
    }

    return _image;
  }
 /**
  * Resolves the URI; if absolute, leaves as is, if relative, returns an absolute URI based on the
  * baseUrl for the agent.
  *
  * @param uri A URI, possibly relative.
  * @return A URI as String, resolved, or null if there was an exception (for example if the URI is
  *     malformed).
  */
 public String resolveURI(String uri) {
   if (uri == null) return null;
   String ret = null;
   if (_baseURL == null) { // first try to set a base URL
     try {
       URL result = new URL(uri);
       setBaseURL(result.toExternalForm());
     } catch (MalformedURLException e) {
       try {
         setBaseURL(new File(".").toURI().toURL().toExternalForm());
       } catch (Exception e1) {
         XRLog.exception(
             "The default NaiveUserAgent doesn't know how to resolve the base URL for " + uri);
         return null;
       }
     }
   }
   // test if the URI is valid; if not, try to assign the base url as its parent
   try {
     return new URL(uri).toString();
   } catch (MalformedURLException e) {
     XRLog.load(
         "Could not read "
             + uri
             + " as a URL; may be relative. Testing using parent URL "
             + _baseURL);
     try {
       URL result = new URL(new URL(_baseURL), uri);
       ret = result.toString();
     } catch (MalformedURLException e1) {
       XRLog.exception(
           "The default NaiveUserAgent cannot resolve the URL "
               + uri
               + " with base URL "
               + _baseURL);
     }
   }
   return ret;
 }
 public void stopLoading() {
   if (_loadQueue != null) {
     XRLog.load("By request, clearing pending items from load queue: " + _loadQueue.size());
     _loadQueue.reset();
   }
 }