Beispiel #1
0
/**
 * gwy_surface_copy_units_to_data_field:
 * @surface: A surface.
 * @data_field: A two-dimensional data field.
 *
 * Sets lateral and value units of a data field to match a surface.
 *
 * Since: 2.46
 **/
void
gwy_surface_copy_units_to_data_field(GwySurface *surface,
                                     GwyDataField *data_field)
{
    Surface *priv;

    g_return_if_fail(GWY_IS_SURFACE(surface));
    g_return_if_fail(GWY_IS_DATA_FIELD(data_field));

    priv = surface->priv;
    if (priv->si_unit_xy && data_field->si_unit_xy)
        gwy_serializable_clone(G_OBJECT(priv->si_unit_xy),
                               G_OBJECT(data_field->si_unit_xy));
    else if (priv->si_unit_xy && !data_field->si_unit_xy)
        data_field->si_unit_xy = gwy_si_unit_duplicate(priv->si_unit_xy);
    else if (!priv->si_unit_xy && data_field->si_unit_xy)
        GWY_OBJECT_UNREF(data_field->si_unit_xy);

    if (priv->si_unit_z && data_field->si_unit_z)
        gwy_serializable_clone(G_OBJECT(priv->si_unit_z),
                               G_OBJECT(data_field->si_unit_z));
    else if (priv->si_unit_z && !data_field->si_unit_z)
        data_field->si_unit_z = gwy_si_unit_duplicate(priv->si_unit_z);
    else if (!priv->si_unit_z && data_field->si_unit_z)
        GWY_OBJECT_UNREF(data_field->si_unit_z);
}
Beispiel #2
0
static void
copy_info(GwySurface *dest,
          const GwySurface *src)
{
    Surface *dpriv = dest->priv, *spriv = src->priv;

    /* SI Units can be NULL */
    if (spriv->si_unit_xy && dpriv->si_unit_xy)
        gwy_serializable_clone(G_OBJECT(spriv->si_unit_xy),
                               G_OBJECT(dpriv->si_unit_xy));
    else if (spriv->si_unit_xy && !dpriv->si_unit_xy)
        dpriv->si_unit_xy = gwy_si_unit_duplicate(spriv->si_unit_xy);
    else if (!spriv->si_unit_xy && dpriv->si_unit_xy)
        GWY_OBJECT_UNREF(dpriv->si_unit_xy);

    if (spriv->si_unit_z && dpriv->si_unit_z)
        gwy_serializable_clone(G_OBJECT(spriv->si_unit_z),
                               G_OBJECT(dpriv->si_unit_z));
    else if (spriv->si_unit_z && !dpriv->si_unit_z)
        dpriv->si_unit_z = gwy_si_unit_duplicate(spriv->si_unit_z);
    else if (!spriv->si_unit_z && dpriv->si_unit_z)
        GWY_OBJECT_UNREF(dpriv->si_unit_z);

    dpriv->cached_ranges = spriv->cached_ranges;
    dpriv->min = spriv->min;
    dpriv->max = spriv->max;

    dpriv->cached_checksum = spriv->cached_checksum;
    gwy_assign(dpriv->checksum, spriv->checksum, G_N_ELEMENTS(spriv->checksum));
}
Beispiel #3
0
static void
gwy_surface_dispose(GObject *object)
{
    GwySurface *surface = GWY_SURFACE(object);
    GWY_OBJECT_UNREF(surface->priv->si_unit_xy);
    GWY_OBJECT_UNREF(surface->priv->si_unit_z);
    G_OBJECT_CLASS(gwy_surface_parent_class)->dispose(object);
}
Beispiel #4
0
/**
 * gwy_data_field_correlate_finalize:
 * @state: Correlation iterator.
 *
 * Destroys a correlation iterator, freeing all resources.
 **/
void
gwy_data_field_correlate_finalize(GwyComputationState *cstate)
{
    GwyCorrelationState *state = (GwyCorrelationState*)cstate;

    GWY_OBJECT_UNREF(state->data_field);
    GWY_OBJECT_UNREF(state->kernel_field);
    GWY_OBJECT_UNREF(state->score);
    GWY_OBJECT_UNREF(state->avg);
    GWY_OBJECT_UNREF(state->rms);
    g_free(state);
}
Beispiel #5
0
static void
immerse_controls_destroy(ImmerseControls *controls)
{
    gtk_widget_destroy(controls->dialog);
    gwy_si_unit_value_format_free(controls->vf);
    g_object_unref(controls->mydata);
    gdk_cursor_unref(controls->near_cursor);
    gdk_cursor_unref(controls->move_cursor);
    GWY_OBJECT_UNREF(controls->detail);
}
Beispiel #6
0
static void
gwy_coords_view_finalize(GObject *object)
{
    GwyCoordsView *view = GWY_COORDS_VIEW(object);
    CoordsView *priv = view->priv;
    free_dim_info(view);
    GWY_OBJECT_UNREF(priv->pixel_format);
    // The columns should be already destroyed.
    g_assert(!priv->column_info);
    G_OBJECT_CLASS(gwy_coords_view_parent_class)->finalize(object);
}
Beispiel #7
0
static void
gwy_adjustment_dispose(GObject *object)
{
    Adjustment *priv = GWY_ADJUSTMENT(object)->priv;
    // Clean up in case the binding survives us somehow.
    if (priv->binding) {
        g_object_remove_weak_pointer(G_OBJECT(priv->binding),
                                     (gpointer*)&priv->binding);
        GWY_OBJECT_UNREF(priv->binding);
    }
    G_OBJECT_CLASS(gwy_adjustment_parent_class)->dispose(object);
}
Beispiel #8
0
/**
 * gwy_surface_set_si_unit_z:
 * @surface: A surface.
 * @si_unit: SI unit to be set.
 *
 * Sets the SI unit corresponding to the "height" (Z) dimension of a surface.
 *
 * It does not assume a reference on @si_unit, instead it adds its own
 * reference.
 *
 * Since: 2.45
 **/
void
gwy_surface_set_si_unit_z(GwySurface *surface,
                          GwySIUnit *si_unit)
{
    g_return_if_fail(GWY_IS_SURFACE(surface));
    g_return_if_fail(GWY_IS_SI_UNIT(si_unit));
    if (surface->priv->si_unit_z == si_unit)
        return;

    GWY_OBJECT_UNREF(surface->priv->si_unit_z);
    g_object_ref(si_unit);
    surface->priv->si_unit_z = si_unit;
}
Beispiel #9
0
/**
 * gwy_resource_classes_finalize:
 *
 * Destroys the inventories of all resource classes.
 *
 * This function makes the affected resource classes unusable.  Its purpose is
 * to faciliate reference leak debugging by destroying a large number of
 * objects that normally live forever.
 *
 * Note static resource classes that never called gwy_resource_class_load()
 * are excluded.
 *
 * Since: 2.8
 **/
void
gwy_resource_classes_finalize(void)
{
    GSList *l;

    for (l = all_resources; l; l = g_slist_next(l)) {
        GwyResourceClass *klass;

        klass = g_type_class_ref((GType)GPOINTER_TO_SIZE(all_resources->data));
        GWY_OBJECT_UNREF(klass->inventory);
    }

    g_slist_free(all_resources);
    all_resources = NULL;
}
Beispiel #10
0
static void
free_dim_info(GwyCoordsView *view)
{
    CoordsView *priv = view->priv;
    if (!priv->dim_info)
        return;

    guint dimension = priv->coords_class->dimension;
    for (guint i = 0; i < dimension; i++) {
        DimInfo *info = priv->dim_info + i;
        GWY_SIGNAL_HANDLER_DISCONNECT(info->vf, info->vf_notify_id);
        GWY_OBJECT_UNREF(info->vf);
        GWY_FREE(info->units);
    }
    g_slice_free1(dimension*sizeof(DimInfo), priv->dim_info);
    priv->dim_info = NULL;
    g_type_class_unref(priv->coords_class);
}
Beispiel #11
0
void
test_user_grain_value_save(void)
{
    GwyUserGrainValue *usergrainvalue = make_test_grain_value();
    GwyResource *resource = GWY_RESOURCE(usergrainvalue);

    GError *error = NULL;
    gwy_resource_set_filename(resource, "Bloat3");
    g_assert(gwy_resource_save(resource, &error));
    g_assert_no_error(error);
    g_test_queue_destroy((GDestroyNotify)g_unlink, "Bloat3");
    resource_check_file(resource, "Bloat3");
    GWY_OBJECT_UNREF(usergrainvalue);
    user_grain_value_load_check("Bloat3", "Bloat", "The Bloat Group",
                                "V_0/L_b0^3",
                                -1, 0, 1,
                                GWY_GRAIN_VALUE_SAME_UNITS_LATERAL, FALSE);
}
Beispiel #12
0
/**
 * gwy_data_field_crosscorrelate_finalize:
 * @state: Cross-correlation iterator.
 *
 * Destroys a cross-correlation iterator, freeing all resources.
 **/
void
gwy_data_field_crosscorrelate_finalize(GwyComputationState *cstate)
{
    GwyCrossCorrelationState *state = (GwyCrossCorrelationState*)cstate;

    GWY_OBJECT_UNREF(state->data_field1);
    GWY_OBJECT_UNREF(state->data_field2);
    GWY_OBJECT_UNREF(state->x_dist);
    GWY_OBJECT_UNREF(state->y_dist);
    GWY_OBJECT_UNREF(state->score);
    GWY_OBJECT_UNREF(state->weights);
    g_free(state);
}
Beispiel #13
0
static GObject*
gwy_surface_deserialize(const guchar *buffer,
                        gsize size,
                        gsize *position)
{
    guint32 datasize = 0;
    GwySIUnit *si_unit_xy = NULL, *si_unit_z = NULL;
    GwySurface *surface;
    GwyXYZ *data = NULL;
    GwySerializeSpec spec[] = {
        { 'o', "si_unit_xy", &si_unit_xy, NULL, },
        { 'o', "si_unit_z", &si_unit_z, NULL, },
        { 'D', "data", &data, &datasize, },
    };

    g_return_val_if_fail(buffer, NULL);

    if (!gwy_serialize_unpack_object_struct(buffer, size, position,
                                            GWY_SURFACE_TYPE_NAME,
                                            G_N_ELEMENTS(spec), spec)) {
        g_free(data);
        GWY_OBJECT_UNREF(si_unit_xy);
        GWY_OBJECT_UNREF(si_unit_z);
        return NULL;
    }
    if (datasize % 3 != 0) {
        g_critical("Serialized %s data size %u not a multiple of 3",
                   GWY_SURFACE_TYPE_NAME, datasize);
        g_free(data);
        GWY_OBJECT_UNREF(si_unit_xy);
        GWY_OBJECT_UNREF(si_unit_z);
        return NULL;
    }

    surface = gwy_surface_new();

    g_free(surface->data);
    surface->data = data;
    surface->n = datasize/3;

    if (si_unit_z) {
        GWY_OBJECT_UNREF(surface->priv->si_unit_z);
        surface->priv->si_unit_z = si_unit_z;
    }
    if (si_unit_xy) {
        GWY_OBJECT_UNREF(surface->priv->si_unit_xy);
        surface->priv->si_unit_xy = si_unit_xy;
    }

    return (GObject*)surface;
}
Beispiel #14
0
static void
immerse_update_detail_pixbuf(ImmerseControls *controls)
{
    GwyContainer *data;
    GwyDataField *dfield;
    GwyGradient *gradient;
    GdkPixbuf *pixbuf;
    const guchar *name;
    gchar *key;
    GQuark quark;
    gint w, h, xres, yres;

    GWY_OBJECT_UNREF(controls->detail);
    data = gwy_app_data_browser_get(controls->args->detail.datano);
    if (!data)
        return;

    quark = gwy_app_get_data_key_for_id(controls->args->detail.id);
    dfield = gwy_container_get_object(data, quark);
    gwy_data_view_coords_real_to_xy(GWY_DATA_VIEW(controls->view),
                                    gwy_data_field_get_xreal(dfield),
                                    gwy_data_field_get_yreal(dfield),
                                    &w, &h);
    gwy_debug("%dx%d", w, h);
    w = MAX(w, 2);
    h = MAX(h, 2);

    key = g_strdup_printf("/%d/base/palette", controls->args->image.id);
    name = NULL;
    data = gwy_app_data_browser_get(controls->args->image.datano);
    gwy_container_gis_string_by_name(data, key, &name);
    g_free(key);
    gradient = gwy_gradients_get_gradient(name);

    /* Handle real-square properly by using an intermediate
     * pixel-square pixbuf with sufficient resolution */
    xres = gwy_data_field_get_xres(dfield);
    yres = gwy_data_field_get_yres(dfield);
    pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, xres, yres);
    gwy_pixbuf_draw_data_field(pixbuf, dfield, gradient);
    controls->detail = gdk_pixbuf_scale_simple(pixbuf, w, h, GDK_INTERP_TILES);
    g_object_unref(pixbuf);
}
Beispiel #15
0
static gboolean
set_coords(GwyCoordsView *view,
           GwyCoords *coords)
{
    CoordsView *priv = view->priv;
    if (!gwy_set_member_object(view, coords, GWY_TYPE_COORDS, &priv->coords,
                               NULL))
        return FALSE;

    GWY_OBJECT_UNREF(priv->store);
    if (!coords) {
        gtk_tree_view_set_model(GTK_TREE_VIEW(view), NULL);
        return TRUE;
    }
    set_coords_type(view, G_OBJECT_TYPE(coords));

    priv->store = gwy_list_store_new(GWY_LISTABLE(coords));
    g_object_ref_sink(priv->store);
    gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(priv->store));
    return TRUE;
}
Beispiel #16
0
static void
user_grain_value_load_check(const gchar *filename,
                            const gchar *expected_name,
                            const gchar *expected_group,
                            const gchar *expected_formula,
                            gint expected_power_x,
                            gint expected_power_y,
                            gint expected_power_z,
                            GwyGrainValueSameUnits expected_same_units,
                            gboolean expected_is_angle)
{
    GError *error = NULL;
    GwyResource *resource = gwy_resource_load(filename,
                                              GWY_TYPE_USER_GRAIN_VALUE,
                                              TRUE, &error);
    g_assert_no_error(error);
    g_assert(GWY_IS_USER_GRAIN_VALUE(resource));
    GwyUserGrainValue *usergrainvalue = GWY_USER_GRAIN_VALUE(resource);
    g_assert_cmpstr(gwy_resource_get_name(resource), ==, expected_name);
    g_assert_cmpstr(gwy_user_grain_value_get_group(usergrainvalue),
                    ==, expected_group);
    g_assert(!gwy_resource_is_managed(resource));
    g_assert(gwy_resource_is_modifiable(resource));

    resource_check_file(resource, filename);

    g_assert_cmpstr(gwy_user_grain_value_get_formula(usergrainvalue),
                    ==, expected_formula);
    g_assert_cmpint(gwy_user_grain_value_get_power_x(usergrainvalue),
                    ==, expected_power_x);
    g_assert_cmpint(gwy_user_grain_value_get_power_y(usergrainvalue),
                    ==, expected_power_y);
    g_assert_cmpint(gwy_user_grain_value_get_power_z(usergrainvalue),
                    ==, expected_power_z);
    g_assert_cmpuint(gwy_user_grain_value_get_same_units(usergrainvalue),
                     ==, expected_same_units);
    g_assert_cmpint(gwy_user_grain_value_get_is_angle(usergrainvalue),
                    ==, expected_is_angle);
    GWY_OBJECT_UNREF(usergrainvalue);
}
Beispiel #17
0
static void
free_header(BurleighExpHeader *header)
{
    GWY_OBJECT_UNREF(header->xyunits);
    GWY_OBJECT_UNREF(header->zunits);
}
Beispiel #18
0
static GwyContainer*
rawxyz_load(const gchar *filename,
            GwyRunType mode,
            GError **error)
{
    GwyContainer *settings, *container = NULL;
    GwySurface *surface = NULL;
    RawXYZArgs args;
    GwySIUnit *unit;
    gint power10;
    gdouble q;
    gchar *buffer = NULL;
    gsize size;
    GError *err = NULL;
    gboolean ok;
    guint k;

    if (!g_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        goto fail;
    }

    surface = read_xyz_points(buffer);
    g_free(buffer);
    if (!surface->n) {
        err_NO_DATA(error);
        goto fail;
    }

    settings = gwy_app_settings_get();
    rawxyz_load_args(settings, &args);
    if (mode == GWY_RUN_INTERACTIVE) {
        ok = rawxyz_dialog(&args, surface);
        rawxyz_save_args(settings, &args);
        if (!ok) {
            err_CANCELLED(error);
            goto fail;
        }
    }

    unit = gwy_si_unit_new_parse(args.xy_units, &power10);
    if (power10) {
        q = pow10(power10);
        for (k = 0; k < surface->n; k++) {
            surface->data[k].x *= q;
            surface->data[k].y *= q;
        }
        gwy_surface_invalidate(surface);
    }
    gwy_serializable_clone(G_OBJECT(unit),
                           G_OBJECT(gwy_surface_get_si_unit_xy(surface)));

    unit = gwy_si_unit_new_parse(args.z_units, &power10);
    if (power10) {
        q = pow10(power10);
        for (k = 0; k < surface->n; k++)
            surface->data[k].z *= q;
        gwy_surface_invalidate(surface);
    }
    gwy_serializable_clone(G_OBJECT(unit),
                           G_OBJECT(gwy_surface_get_si_unit_z(surface)));

    container = gwy_container_new();
    gwy_container_set_object(container, gwy_app_get_surface_key_for_id(0),
                             surface);
    gwy_app_xyz_title_fall_back(container, 0);
    gwy_file_xyz_import_log_add(container, 0, NULL, filename);

fail:
    g_free(args.xy_units);
    g_free(args.z_units);
    GWY_OBJECT_UNREF(surface);

    return container;
}
Beispiel #19
0
static GwyContainer*
asc_load(const gchar *filename,
         G_GNUC_UNUSED GwyRunType mode,
         GError **error)
{
    GwyContainer *container = NULL;
    GwyDataField *dfield = NULL, *mfield = NULL;
    GwyTextHeaderParser parser;
    GwySIUnit *unit;
    gchar *line, *p, *value, *buffer = NULL;
    GHashTable *hash = NULL;
    gsize size;
    GError *err = NULL;
    gdouble xreal, yreal, q;
    gint i, xres, yres;
    gdouble *data;

    if (!g_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        goto fail;
    }

    p = buffer;
    line = gwy_str_next_line(&p);
    if (!gwy_strequal(line, MAGIC_BARE)) {
        err_FILE_TYPE(error, "SPIP ASCII data");
        goto fail;
    }

    gwy_clear(&parser, 1);
    parser.line_prefix = "#";
    parser.key_value_separator = "=";
    parser.terminator = "# Start of Data:";
    parser.error = &header_error;
    parser.end = &header_end;
    if (!(hash = gwy_text_header_parse(p, &parser, &p, &err))) {
        g_propagate_error(error, err);
        goto fail;
    }
    if (!require_keys(hash, error,
                      "x-pixels", "y-pixels", "x-length", "y-length",
                      NULL))
        goto fail;

    xres = atoi(g_hash_table_lookup(hash, "x-pixels"));
    yres = atoi(g_hash_table_lookup(hash, "y-pixels"));
    if (err_DIMENSION(error, xres) || err_DIMENSION(error, yres))
        goto fail;

    xreal = Nanometer * g_ascii_strtod(g_hash_table_lookup(hash, "x-length"),
                                       NULL);
    yreal = Nanometer * g_ascii_strtod(g_hash_table_lookup(hash, "y-length"),
                                       NULL);
    /* Use negated positive conditions to catch NaNs */
    if (!((xreal = fabs(xreal)) > 0)) {
        g_warning("Real x size is 0.0, fixing to 1.0");
        xreal = 1.0;
    }
    if (!((yreal = fabs(yreal)) > 0)) {
        g_warning("Real y size is 0.0, fixing to 1.0");
        yreal = 1.0;
    }

    dfield = gwy_data_field_new(xres, yres, xreal, yreal, FALSE);

    unit = gwy_si_unit_new("m");
    gwy_data_field_set_si_unit_xy(dfield, unit);
    g_object_unref(unit);

    if ((value = g_hash_table_lookup(hash, "z-unit"))) {
        gint power10;

        unit = gwy_si_unit_new_parse(value, &power10);
        gwy_data_field_set_si_unit_z(dfield, unit);
        g_object_unref(unit);
        q = pow10(power10);
    }
    else if ((value = g_hash_table_lookup(hash, "Bit2nm"))) {
        q = Nanometer * g_ascii_strtod(value, NULL);
        unit = gwy_si_unit_new("m");
        gwy_data_field_set_si_unit_z(dfield, unit);
        g_object_unref(unit);
    }
    else
        q = 1.0;

    data = gwy_data_field_get_data(dfield);
    value = p;
    for (i = 0; i < xres*yres; i++) {
        data[i] = q*g_ascii_strtod(value, &p);
        if (p == value && (!*p || g_ascii_isspace(*p))) {
            g_set_error(error, GWY_MODULE_FILE_ERROR,
                        GWY_MODULE_FILE_ERROR_DATA,
                        _("End of file reached when reading sample #%d of %d"),
                        i, xres*yres);
            goto fail;
        }
        if (p == value) {
            g_set_error(error, GWY_MODULE_FILE_ERROR,
                        GWY_MODULE_FILE_ERROR_DATA,
                        _("Malformed data encountered when reading sample "
                          "#%d of %d"),
                        i, xres*yres);
            goto fail;
        }
        value = p;
    }

    if ((value = g_hash_table_lookup(hash, "voidpixels")) && atoi(value)) {
        mfield = gwy_data_field_new_alike(dfield, FALSE);
        data = gwy_data_field_get_data(mfield);
        value = p;
        for (i = 0; i < xres*yres; i++) {
            data[i] = 1.0 - g_ascii_strtod(value, &p);
            value = p;
        }
        if (!gwy_app_channel_remove_bad_data(dfield, mfield))
            GWY_OBJECT_UNREF(mfield);
    }

    container = gwy_container_new();

    gwy_container_set_object(container, gwy_app_get_data_key_for_id(0), dfield);

    if (mfield) {
        gwy_container_set_object(container, gwy_app_get_mask_key_for_id(0),
                                 mfield);
        g_object_unref(mfield);
    }

    gwy_file_channel_import_log_add(container, 0, NULL, filename);

fail:
    GWY_OBJECT_UNREF(dfield);
    g_free(buffer);
    if (hash)
        g_hash_table_destroy(hash);

    return container;
}
Beispiel #20
0
static GwyContainer*
csmfile_load(const gchar *filename,
             G_GNUC_UNUSED GwyRunType mode,
             GError **error)
{
    GwyContainer *meta, *container = NULL;
    GHashTable *hash = NULL;
    guchar *d24, *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    GwyDataField *dfield = NULL;
    guint xres, yres, bmpsize, header_size, maxval, i, j;
    gdouble real, zmin, zmax, q, z0;
    GwyTextHeaderParser parser;
    GwySIUnit *unit = NULL;
    gchar *value, *end, *header = NULL;
    gdouble *data;
    gint power10;

    if (!gwy_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }

    if (size < BMP_HEADER_SIZE) {
        err_TOO_SHORT(error);
        goto fail;
    }
    if (!read_bmp_header(buffer, &xres, &yres, &bmpsize)
        || size <= bmpsize) {
        err_FILE_TYPE(error, "CSM");
        goto fail;
    }

    header_size = size - bmpsize;
    header = g_new(gchar, header_size + 1);
    memcpy(header, buffer + bmpsize, header_size);
    header[header_size] = '\0';

    gwy_clear(&parser, 1);
    parser.key_value_separator = "=";
    hash = gwy_text_header_parse(header, &parser, NULL, NULL);

    /* Do NOT use the fields Image width, Image height from the added footer.
     * Even though it is specifically added by Benyuan it can disagree with the
     * BMP diemsions and when they disagree the BMP diemsions are apparently
     * right. */
    if (err_DIMENSION(error, xres))
        goto fail;
    if (err_DIMENSION(error, yres))
        goto fail;

    if (!(value = g_hash_table_lookup(hash, "ScanSize"))) {
        err_MISSING_FIELD(error, "ScanSize");
        goto fail;
    }
    real = g_ascii_strtod(value, NULL);
    /* Use negated positive conditions to catch NaNs */
    if (!((real = fabs(real)) > 0)) {
        g_warning("Real size is 0.0, fixing to 1.0");
        real = 1.0;
    }

    if (!(value = g_hash_table_lookup(hash, "HeightScale"))) {
        err_MISSING_FIELD(error, "HeightScale");
        goto fail;
    }
    zmax = g_ascii_strtod(value, &end);
    unit = gwy_si_unit_new_parse(end, &power10);

    /* Optional stuff for which we try to fall back. */
    if (!(value = g_hash_table_lookup(hash, "StartHeightScale")))
        zmin = 0.0;
    else
        zmin = g_ascii_strtod(value, NULL);

    if (!(value = g_hash_table_lookup(hash, "MaxValue")))
        maxval = 0xffff;
    else
        maxval = MAX(atoi(value), 1);


    dfield = gwy_data_field_new(xres, yres, real*Nanometre, real*Nanometre,
                                FALSE);
    data = gwy_data_field_get_data(dfield);

    d24 = buffer + BMP_HEADER_SIZE;
    q = pow10(power10)*(zmax - zmin)/maxval;
    z0 = pow10(power10)*zmin;
    for (i = 0; i < yres; i++) {
        gdouble *row = data + (yres-1 - i)*xres;
        for (j = 0; j < xres; j++, row++, d24 += 3)
            *row = (d24[0] + 256.0*d24[1])*q + z0;
    }

    gwy_si_unit_set_from_string(gwy_data_field_get_si_unit_xy(dfield), "m");
    gwy_data_field_set_si_unit_z(dfield, unit);

    container = gwy_container_new();
    gwy_container_set_object_by_name(container, "/0/data", dfield);
    g_object_unref(dfield);

    meta = gwy_container_new();
    g_hash_table_foreach(hash, store_meta, meta);
    gwy_container_set_object_by_name(container, "/0/meta", meta);
    g_object_unref(meta);

    if ((value = g_hash_table_lookup(hash, "sTitle"))
        && g_utf8_validate(value, -1, NULL)) {
        gwy_container_set_string_by_name(container, "/0/data/title",
                                         g_strdup(value));
    }
    else
        gwy_app_channel_title_fall_back(container, 0);

    gwy_file_channel_import_log_add(container, 0, NULL, filename);

fail:
    gwy_file_abandon_contents(buffer, size, NULL);
    GWY_OBJECT_UNREF(unit);
    if (header)
        g_free(header);
    if (hash)
        g_hash_table_destroy(hash);

    return container;
}