Am Freitag, 15. Juli 2016, 12:01:21 CEST schrieb Wolfgang Mader:
> Dear all,
> 
> I went through the useless.c iop to get an idea of how an iop is structured.
> Not everything in that file made sense to me. Therefore, I left a couple of
> questions in the iop file I am working on [1].

Having it as a fork in github would have been easier to comment, but let's 
just do it in here.

> Every line with a question starts with //->. I kindly ask you to glance over
> my questions and to answer them if you have the time.
> 
> Greatly appreciate your response.
> Wolfgang
> 
> 
> [1] https://deck.dd-dns.de/cloud/index.php/s/Xekv30CEyy9Fp8C


/*
 *  This file is part of darktable,
 *  copyright (c) 2009--2012 johannes hanika.
 *  copyright (c) 2016 Wolfgang Mader.
 *
 *  darktable is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  darktable is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with darktable.  If not, see <http://www.gnu.org/licenses/>.
 */

//-> On compiling, I get the error
//-> error: can't generate introspection data for type `dt_iop_bw_params_t'.
//-> Is there s.th. wrong with my params_t struct?

you can't have pointers, double or bitfields in the params. reason being that 
those have different padding on different architectures/compilers so that the 
generated params blob won't be universally readable by dt.

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "bauhaus/bauhaus.h"
#include "develop/imageop.h"
#include "gui/gtk.h"
#include "iop/iop_api.h"

#include <gtk/gtk.h>
#include <stdlib.h>

DT_MODULE_INTROSPECTION(1, dt_iop_bw_params_t)

typedef struct dt_iop_bw_params_t
{
  double uc; //reference white
} dt_iop_bw_params_t;

typedef struct dt_iop_bw_gui_data_t
{
  GtkWidget *scale;
} dt_iop_bw_gui_data_t;

typedef struct dt_iop_bw_global_data_t
{
  // this is optionally stored in self->global_data
  // and can be used to alloc globally needed stuff
  // which is needed in gui mode and during processing.

  //-> Does this mean that whatever I store here is accessible from the GUI as 
well as the pixel
  //-> pipeline?
  
yes. however, it seems the real name is self->data.
  
} dt_iop_bw_global_data_t;

const char *name()
{
  return _("Convert to black and white");
}

int flags()
{
  return IOP_FLAGS_INCLUDE_IN_STYLES | IOP_FLAGS_SUPPORTS_BLENDING;
}

int groups()
{
  return IOP_GROUP_COLOR;
}

//-> I ignore this for now. If you have an easy example, I would be happy to 
read it.
// implement this, if you have esoteric output bytes per pixel. default is 
4*float

the output_bpp? yes, just ignore it.

/*
int
output_bpp(dt_iop_module_t *module, dt_dev_pixelpipe_t *pipe, 
dt_dev_pixelpipe_iop_t *piece)
{
  if(pipe->type != DT_DEV_PIXELPIPE_PREVIEW && module->dev->image->filters) 
return sizeof(float);
  else return 4*sizeof(float);
}
*/

//-> I ignore this for now. If you have an easy example, I would be happy to 
read it.

those can be ignored for your case, too. it's only needed when changing the 
size of the image (cropping, adding borders, ...) and in general ugly code.

/** modify regions of interest (optional, per pixel ops don't need this) */
// void modify_roi_out(struct dt_iop_module_t *self, struct 
dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t
// *roi_out, const dt_iop_roi_t *roi_in);
// void modify_roi_in(struct dt_iop_module_t *self, struct 
dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t
// *roi_out, dt_iop_roi_t *roi_in);

//-> How are i and o scaled?
void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, 
const void *const i, void *const o,
             const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const 
roi_out)
{
  // this is called for preview and full pipe separately, each with its own 
pixelpipe piece.
  // get our data struct:
//   dt_iop_bw_params_t *d = (dt_iop_bw_params_t *)piece->data;
  // the total scale is composed of scale before input to the pipeline 
(iscale),
  // and the scale of the roi.

  //-> How does this scaling work. Is scale in [0,1]? If so, does scale = 1 
means full image?
i keep forgetting and just copy&paste code over until it works. and when only 
accessing single pixels you can ignore the scaling stuff.
//   const float scale = piece->iscale / roi_in->scale;

  // how many colors in our buffer?
  //-> How do I know what the pixel values mean. Is gamma correction applied? 
Which color space are
  //-> we using here. Can I request the image in a special color space or do I 
have to convert
  //-> myself?
it depends on where in the pipe you are. most is done in Lab space. this one 
is output rgb however (which shouldn't be used any longer for new modules).
  const int ch = piece->colors;

  for(int j = 0; j < roi_out->height; j++)
  {
    float *in = ((float *)i) + (size_t)ch * roi_in->width * j;
    float *out = ((float *)o) + (size_t)ch * roi_out->width * j;
    for(int i = 0; i < roi_out->width; i++)
    {
      // calculate world space coordinates:
      //-> What are these coordinates?
//       int wi = (roi_in->x + i) * scale, wj = (roi_in->y + j) * scale;

      //-> The following is the original checker board code from useless.c.
      //-> c runs from 0 to 2. Does c run through the three RGB values? If so, 
why do we request the
      //-> number of colors further up with "const int ch = piece->colors;" if 
now the 3 is hard
      //-> coded?
yes, it's the three channels (RGB for this module). using ch instead of 3 
wouldn't work as it's 4 -- there is a 4th channel that you shouldn't touch as 
it contains mask data. So it's more the stride width per pixel than the number 
of colors.

      /*
      if((wi / d->checker_scale + wj / d->checker_scale) & 1)
        for(int c = 0; c < 3; c++) out[c] = 0;
      else
        for(int c = 0; c < 3; c++) out[c] = in[c];
      */

      //Simple bw conversion
      float grayvalue = .33*(in[0] + in[1] + in[2]);
      for(int c = 0; c < 3; ++c) {
        out[c] = grayvalue;
      }
      in += ch;
      out += ch;
    }
  }
}

/** optional: if this exists, it will be called to init new defaults if a new 
image is loaded from film strip
 * mode. */
void reload_defaults(dt_iop_module_t *module)
{
  // change default_enabled depending on type of image, or set new 
default_params even.

  // if this callback exists, it has to write default_params and 
default_enabled.
}

void init(dt_iop_module_t *module)
{
  // we don't need global data:
  module->data = NULL; // malloc(sizeof(dt_iop_bw_global_data_t));
  module->params = calloc(1, sizeof(dt_iop_bw_params_t));
  module->default_params = calloc(1, sizeof(dt_iop_bw_params_t));
  // our module is disabled by default
  // by default:
  module->default_enabled = 0;
  // order has to be changed by editing the dependencies in tools/
iop_dependencies.py
  module->priority = 901; // module order created by iop_dependencies.py, do 
not edit!
  module->params_size = sizeof(dt_iop_bw_params_t);
  module->gui_data = NULL;
  // init defaults:
  dt_iop_bw_params_t tmp = (dt_iop_bw_params_t){ 50 };

  memcpy(module->params, &tmp, sizeof(dt_iop_bw_params_t));
  memcpy(module->default_params, &tmp, sizeof(dt_iop_bw_params_t));
}

void init_global(dt_iop_module_so_t *module)
{
  module->data = malloc(sizeof(dt_iop_bw_global_data_t));
}

void cleanup(dt_iop_module_t *module)
{
  free(module->params);
  module->params = NULL;
}

void cleanup_global(dt_iop_module_so_t *module)
{
  free(module->data);
  module->data = NULL;
}

static void slider_callback(GtkWidget *w, dt_iop_module_t *self)
{
  // this is important to avoid cycles!
  if(darktable.gui->reset) return;
  dt_iop_bw_params_t *p = (dt_iop_bw_params_t *)self->params;
  p->uc = dt_bauhaus_slider_get(w);
  // let core know of the changes
  dt_dev_add_history_item(darktable.develop, self, TRUE);
}

/** gui callbacks, these are needed. */
void gui_update(dt_iop_module_t *self)
{
  // let gui slider match current parameters:
  dt_iop_bw_gui_data_t *g = (dt_iop_bw_gui_data_t *)self->gui_data;
  dt_iop_bw_params_t *p = (dt_iop_bw_params_t *)self->params;
  dt_bauhaus_slider_set(g->scale, p->uc);
}

void gui_init(dt_iop_module_t *self)
{
  // init the slider (more sophisticated layouts are possible with gtk tables 
and boxes):
  self->gui_data = malloc(sizeof(dt_iop_bw_gui_data_t));
  dt_iop_bw_gui_data_t *g = (dt_iop_bw_gui_data_t *)self->gui_data;
  g->scale = dt_bauhaus_slider_new_with_range(self, 1, 100, 1, 50, 0);
  self->widget = g->scale;
  g_signal_connect(G_OBJECT(g->scale), "value-changed", 
G_CALLBACK(slider_callback), self);
}

void gui_cleanup(dt_iop_module_t *self)
{
  // nothing else necessary, gtk will clean up the slider.
  free(self->gui_data);
  self->gui_data = NULL;
}

// modelines: These editor modelines have been set for all relevant files by 
tools/update_modelines.sh
// vim: shiftwidth=2 expandtab tabstop=2 cindent
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode 
cstyle; remove-trailing-spaces modified;

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to