public static function getImageServingUrl($gs_filename, $options = []) {
    $blob_key = self::createGsKey($gs_filename);
    if (!is_array($options)) {
      throw new \InvalidArgumentException('$options must be an array. ' .
          'Actual type: ' . gettype($options));
    }

    $extra_options = array_diff(array_keys($options), array_keys(
         self::$get_image_serving_url_default_options));
    if (!empty($extra_options)) {
      throw new \InvalidArgumentException('Invalid options supplied: ' .
          htmlspecialchars(implode(',', $extra_options)));
    }
    $options = array_merge(self::$get_image_serving_url_default_options,
                           $options);

    # Validate options.
    if (!is_bool($options['crop'])) {
      throw new \InvalidArgumentException(
          '$options[\'crop\'] must be a boolean. ' .
          'Actual type: ' . gettype($options['crop']));
    }
    if ($options['crop'] && is_null($options['size'])) {
      throw new \InvalidArgumentException(
          '$options[\'size\'] must be set because $options[\'crop\'] is true.');
    }
    if (!is_null($options['size'])) {
      $size = $options['size'];
      if (!is_int($size)) {
        throw new \InvalidArgumentException(
            '$options[\'size\'] must be an integer. ' .
            'Actual type: ' . gettype($size));
      }
      if ($size < 0 || $size > self::MAX_IMAGE_SERVING_SIZE) {
        throw new \InvalidArgumentException(
            '$options[\'size\'] must be >= 0 and <= ' .
            self::MAX_IMAGE_SERVING_SIZE .  '. Actual value: ' . $size);
      }
    }
    if (!is_bool($options['secure_url'])) {
      throw new \InvalidArgumentException(
          '$options[\'secure_url\'] must be a boolean. ' .
          'Actual type: ' . gettype($options['secure_url']));
    }

    $req = new ImagesGetUrlBaseRequest();
    $resp = new ImagesGetUrlBaseResponse();
    $req->setBlobKey($blob_key);
    $req->setCreateSecureUrl($options['secure_url']);

    try {
      ApiProxy::makeSyncCall('images',
                             'GetUrlBase',
                             $req,
                             $resp);
    } catch (ApplicationError $e) {
      throw self::imagesApplicationErrorToException($e);
    }
    $url = $resp->getUrl();
    if (!is_null($options['size'])) {
      $url .= ('=s' . $options['size']);
      if ($options['crop']) {
        $url .= '-c';
      }
    }
    return $url;
  }