bool _openslide_try_generic_tiff(openslide_t *osr, TIFF *tiff,
				 struct _openslide_hash *quickhash1) {
  GList *layer_list = NULL;
  int32_t layer_count = 0;
  int32_t *layers = NULL;
  int32_t current_layer = 0;

  if (!TIFFIsTiled(tiff)) {
    goto FAIL; // not tiled
  }

  if (osr) {
    g_hash_table_insert(osr->properties,
			g_strdup(OPENSLIDE_PROPERTY_NAME_VENDOR),
			g_strdup("generic-tiff"));
  }

  // accumulate tiled layers
  current_layer = 0;
  layer_count = 0;
  do {
    if (TIFFIsTiled(tiff)) {
      // get width
      uint32_t width;
      if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width)) {
	// oh no
	continue;
      }

      // confirm it is either the first image, or reduced-resolution
      if (current_layer != 0) {
	uint32_t subfiletype;
	if (!TIFFGetField(tiff, TIFFTAG_SUBFILETYPE, &subfiletype)) {
	  continue;
	}

	if (!(subfiletype & FILETYPE_REDUCEDIMAGE)) {
	  continue;
	}
      }

      // verify that we can read this compression (hard fail if not)
      uint16_t compression;
      if (!TIFFGetField(tiff, TIFFTAG_COMPRESSION, &compression)) {
	g_warning("Can't read compression scheme");
	goto FAIL;
      };
      if (!TIFFIsCODECConfigured(compression)) {
	g_warning("Unsupported TIFF compression: %u", compression);
	goto FAIL;
      }

      // push into list
      struct layer *l = g_slice_new(struct layer);
      l->layer_number = current_layer;
      l->width = width;
      layer_list = g_list_prepend(layer_list, l);
      layer_count++;
    }
    current_layer++;
  } while (TIFFReadDirectory(tiff));

  // sort tiled layers
  layer_list = g_list_sort(layer_list, width_compare);

  // copy layers in, while deleting the list
  layers = g_new(int32_t, layer_count);
  for (int i = 0; i < layer_count; i++) {
    struct layer *l = (struct layer *)layer_list->data;
    layer_list = g_list_delete_link(layer_list, layer_list);

    layers[i] = l->layer_number;
    g_slice_free(struct layer, l);
  }

  g_assert(layer_list == NULL);

  // all set, load up the TIFF-specific ops
  _openslide_add_tiff_ops(osr, tiff,
			  0, NULL,
			  layer_count, layers,
			  _openslide_generic_tiff_tilereader,
			  quickhash1);


  return true;

 FAIL:
  // free the layer list
  for (GList *i = layer_list; i != NULL; i = g_list_delete_link(i, i)) {
    g_slice_free(struct layer, i->data);
  }

  return false;
}
bool _openslide_try_aperio(openslide_t *osr, TIFF *tiff,
			   struct _openslide_hash *quickhash1,
			   GError **err) {
  int32_t level_count = 0;
  int32_t *levels = NULL;
  int32_t i = 0;

  if (!TIFFIsTiled(tiff)) {
    g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FORMAT_NOT_SUPPORTED,
                "TIFF is not tiled");
    goto FAIL;
  }

  char *tagval;
  int tiff_result;
  tiff_result = TIFFGetField(tiff, TIFFTAG_IMAGEDESCRIPTION, &tagval);
  if (!tiff_result ||
      (strncmp(APERIO_DESCRIPTION, tagval, strlen(APERIO_DESCRIPTION)) != 0)) {
    g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_FORMAT_NOT_SUPPORTED,
                "Not an Aperio slide");
    goto FAIL;
  }

  /*
   * http://www.aperio.com/documents/api/Aperio_Digital_Slides_and_Third-party_data_interchange.pdf
   * page 14:
   *
   * The first image in an SVS file is always the baseline image (full
   * resolution). This image is always tiled, usually with a tile size
   * of 240 x 240 pixels. The second image is always a thumbnail,
   * typically with dimensions of about 1024 x 768 pixels. Unlike the
   * other slide images, the thumbnail image is always
   * stripped. Following the thumbnail there may be one or more
   * intermediate "pyramid" images. These are always compressed with
   * the same type of compression as the baseline image, and have a
   * tiled organization with the same tile size.
   *
   * Optionally at the end of an SVS file there may be a slide label
   * image, which is a low resolution picture taken of the slide’s
   * label, and/or a macro camera image, which is a low resolution
   * picture taken of the entire slide. The label and macro images are
   * always stripped.
   */

  // for aperio, the tiled directories are the ones we want
  do {
    if (TIFFIsTiled(tiff)) {
      level_count++;
    }
  } while (TIFFReadDirectory(tiff));
  levels = g_new(int32_t, level_count);

  TIFFSetDirectory(tiff, 0);
  i = 0;
  do {
    tdir_t dir = TIFFCurrentDirectory(tiff);
    if (TIFFIsTiled(tiff)) {
      levels[i++] = dir;
      //g_debug("tiled directory: %d", dir);
    } else {
      // associated image
      const char *name = (dir == 1) ? "thumbnail" : NULL;
      if (!add_associated_image(osr ? osr->associated_images : NULL,
                                name, tiff, err)) {
	g_prefix_error(err, "Can't read associated image: ");
	goto FAIL;
      }
      //g_debug("associated image: %d", dir);
    }

    // check depth
    uint32_t depth;
    tiff_result = TIFFGetField(tiff, TIFFTAG_IMAGEDEPTH, &depth);
    if (tiff_result && depth != 1) {
      // we can't handle depth != 1
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA,
                  "Cannot handle ImageDepth=%d", depth);
      goto FAIL;
    }

    // check compression
    uint16_t compression;
    if (!TIFFGetField(tiff, TIFFTAG_COMPRESSION, &compression)) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA,
                  "Can't read compression scheme");
      goto FAIL;
    }
    if ((compression != APERIO_COMPRESSION_JP2K_YCBCR) &&
        (compression != APERIO_COMPRESSION_JP2K_RGB) &&
        !TIFFIsCODECConfigured(compression)) {
      g_set_error(err, OPENSLIDE_ERROR, OPENSLIDE_ERROR_BAD_DATA,
                  "Unsupported TIFF compression: %u", compression);
      goto FAIL;
    }
  } while (TIFFReadDirectory(tiff));

  // read properties
  if (osr) {
    TIFFSetDirectory(tiff, 0);
    TIFFGetField(tiff, TIFFTAG_IMAGEDESCRIPTION, &tagval); // XXX? should be safe, we just did it
    char **props = g_strsplit(tagval, "|", -1);
    add_properties(osr->properties, props);
    g_strfreev(props);
  }

  // special jpeg 2000 aperio thing (with fallback)
  _openslide_add_tiff_ops(osr, tiff, 0, 0, NULL, level_count, levels,
			  aperio_tiff_tilereader,
			  quickhash1);
  return true;

 FAIL:
  g_free(levels);
  return false;
}