1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
|
\documentclass[a4paper,twoside]{scrartcl}
\usepackage{graphicx,color}
\graphicspath{{images/}{../htdocs/images/header/}}
\usepackage{mparhack}
% \usepackage[a4paper]{geometry}
% \geometry{%
% twoside,%
% %height=.7\paperheight,%
% %width=.7\paperwidth,%
% top=.1\paperheight,%
% bottom=.2\paperheight,%
% left=.1\paperwidth,%
% right=.2\paperwidth%
% %bindingoffset
% }
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead{}
\fancyfoot{}
\fancyfoot[LE,RO]{\iffloatpage{}{\thepage}}
\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{0pt}
\setlength{\oddsidemargin}{.1\paperwidth}
\setlength{\evensidemargin}{.2\paperwidth}
\setlength{\headheight}{.1\paperheight}
\setlength{\headsep}{0pt}
\setlength{\topmargin}{0pt}
\setlength{\voffset}{-1in}
\setlength{\hoffset}{-1in}
\setlength{\textwidth}{.7\paperwidth}
\setlength{\textheight}{.7\paperheight}
\setlength{\marginparwidth}{.1\paperwidth}
\newcommand{\nicesection}[2]{%
\cleardoublepage
\fbox{\includegraphics[width=\linewidth]{#1}}%
\vspace*{-1em}%
\section{\hfill #2}
\hrule
\vspace*{\baselineskip}%
}
\fboxsep0pt
\setlength{\parindent}{0pt}
\newcommand{\todo}[1]{{\color{red}\bf TODO: #1}}
\newcommand{\comment}[1]{}
\definecolor{codecol}{rgb}{0.1,0.2,0.5}
\newcommand{\code}[1]{\texttt{\color{codecol}#1}}
\frenchspacing
% \usepackage[T1]{fontenc}
% \usepackage[condensed,math]{iwona}
%\usepackage[T1]{fontenc}
%\usepackage[urw-garamond]{mathdesign}
\usepackage[T1]{fontenc}
\usepackage[scaled]{helvet}
\renewcommand{\familydefault}{\sfdefault}
%\setkomafont{sectioning}{\sfdefault}
\title{darktable Programmer's Guide}
\author{hanatos}
\begin{document}
\fbox{\includegraphics[width=\linewidth]{header12}}%
\vspace*{-1em}%
\section*{\hfill darktable Programmer's Guide}
\hrule
\vspace*{4\baselineskip}
{\hfill version \input version.tex }
\thispagestyle{empty}
\newpage
\tableofcontents
\nicesection{header1}{Introduction}
\label{sec:introduction}
\nicesection{header2}{System Layout}
\resizebox{\linewidth}{!}{\input{graphs/system.pdftex_t}}
\subsection{Module Descriptions}
\begin{description}
\item[gui] using gtk2 and cairo.
\end{description}
\begin{description}
\item[control]
\item[control, scheduler] using pthreads and a custom job queuing system.
\end{description}
\begin{description}
\item[image]
\item[image cache]
\item[mipmap cache]
\item[db]
\item[imageio]
\end{description}
\begin{description}
\item[library] this is the lighttable module.
\item[film]
\end{description}
\begin{description}
\item[develop] this implements the darktable.
\item[pixelpipe]
\item[image operation (iop) modules, aka plug-ins]
\end{description}
\newpage
\subsection{Image Loading Data Flow}
\paragraph{Related Modules}
\begin{itemize}
\item imageio \code{dt\_imageio\_*}
\item db (messily hidden in sqlite3 statements all over the place)
\item image cache \code{dt\_image\_cache\_*}
\item mip cache \code{dt\_mipmap\_cache\_*}
\item image \code{dt\_image\_t}
\end{itemize}
\paragraph{in image cache}
\begin{itemize}
\item \code{dt\_image\_import}
db $\rightarrow$ image struct, trigger imageio disk $\rightarrow$ image entry in db if miss,
metadata through libraw/magick
\end{itemize}
\paragraph{in mip cache}
\begin{itemize}
\item \code{dt\_image\_get}
db $\rightarrow$ mip[0--4], trigger imageio disk $\rightarrow$ mip4 and mip4 $\rightarrow$ mip[0-3] if miss.
db $\rightarrow$ mipf, trigger mip4 $\rightarrow$ mipf or pixels $\rightarrow$ mipf if miss.
\end{itemize}
\paragraph{in imageio}
\begin{itemize}
\item \code{dt\_imageio\_open\_preview}
disk $\rightarrow$ mip[0--4] entries in db (load thumbnail).
\begin{itemize}
\item[\todo{}] \code{dt\_imageio\_open\_ldr\_preview}
\item \code{dt\_imageio\_open\_raw\_preview}
\end{itemize}
\item mip4 $\rightarrow$ mipf (by guessed reverse gamma) \code{dt\_image\_preview\_to\_raw}.
\item full pixels $\rightarrow$ mipf (downscaling) \code{dt\_image\_raw\_to\_preview}.
\item mip4 $\rightarrow$ mip[0--3] \code{dt\_image\_update\_mipmaps}.
\end{itemize}
image import: only load \code{dt\_image\_t} and mip[0--4] to database.
\code{dt\_image\_get}: check mipmap cache, check database, else \code{dt\_imageio\_open[\_preview]}.
The buffers are as follows:
\begin{description}
\item[\code{DT\_IMAGE\_MIP0-4}] are used for rendering in light table mode. These are either loaded from the embedded
thumbnail in the raw files (if the \code{never\_use\_embedded\_thumb} key is not set), or store small versions of
processed buffers.
The function \code{dt\_imageio\_open\_preview} will fill these low-dynamic range buffers ready for display. If a history
stack is present (i.e.\ the image is {\em altered}), this function will load \code{MIPF} instead.
\item[\code{DT\_IMAGE\_MIPF}] is used by the preview pipe or by pipelines spawned by the light table when creating
\code{DT\_IMAGE\_MIP0-4}. It stores a capped-size four float per pixel representation of the input image.
This buffer is needed to reduce the overall mem requirements and still process multiple thumbnails of large images
in parallel. darktable rather stores and prefetches a few low-resolution \code{MIPF} buffers instead of full images.
\code{MIPF} can never be explicitly loaded.
\code{MIPF} are written if a prefetch is requested for fast darkroom mode entering later on, or by \code{dt\_imageio\_open\_preview}
if the image is altered. It is also written if a full image is requested, to avoid duplicate file accesses in this case.
\item[\code{DT\_IMAGE\_FULL}] is the full image buffer as read from disk. That is, raw images will store one \code{uint16\_t}
per pixel, mosaiced input data, while high dynamic range input will result in a four float buffer.
\end{description}
\newpage
\subsection{Cache Interfaces}
\subsubsection{Image Cache}
\code{dt\_image\_cache\_t}
\subsubsection{Mipmap Cache}
\code{dt\_image\_t}
\begin{description}
\item{\code{dt\_image\_get}} get buffer or, if missed, a lower resolution (launch job in bg for correct resolution)
\item{\code{dt\_image\_load}} Load buffer in this thread, return exactly this resolution.
No locking is performed, so be sure to acquire the lock using \code{dt\_image\_alloc} before.
\end{description}
\subsubsection{Homebrew Pixelpipe Cache}
\newpage
\subsection{Plug-in Interface}
For examples, have a look at \code{src/iop/*.\{h,c\}}, and also see the method declarations in \code{src/common/imageop.h}.
Pixel processing is done in pipelines (\code{src/develop/pixelpipe\_hb.c}) with fixed order of the modules,
observe the current status with e.g.
\code{grep priority src/iop/*.c | sort -t '=' -k 2}.
At the time of this writing, the main pixel pipeline stack is organised as follows:
\medskip
\begin{tabular}{lrl}
module name & priority & input color format \\
\hline
rawimport & 100 & - \\
exposure & 150 & fff input color space \\
temperature & 200 & fff input color space \\
highlights & 250 & fff input color space \\
basecurve & 260 & fff input color space \\
profile\_gamma & 299 & fff input color space \\
colorin & 300 & fff input color space \\
colortransfer & 350 & fff $L^*a^*b^*$ \\
equalizer & 500 & fff $L^*a^*b^*$ \\
monochrome & 550 & fff $L^*a^*b^*$ \\
tonecurve & 700 & fff $L^*a^*b^*$ \\
colorzones & 750 & fff $L^*a^*b^*$ \\
colorcorrection& 800 & fff $L^*a^*b^*$ \\
colorout & 900 & fff $L^*a^*b^*$ \\
lens & 940 & fff output color space \\
clipping & 950 & fff output color space \\
grain & 965 & fff output color space \\
clahe & 966 & fff output color space \\
velvia & 967 & fff output color space \\
splittoning & 968 & fff output color space \\
vignette & 970 & fff output color space \\
sharpen & 990 & fff output color space \\
gamma & 1000 & fff output color space \\
& - & ccc output color space \\
\end{tabular}
\medskip
where fff is $3\times32$ bit float, ccc is $3\times8$ bit int, and the input
color space depends on your camera or type of input data, and the output color space will
be sRGB (including gamma correction, i.e.\ it is non-linear) most of the time.
$L^*a^*b^*$ is a special variant of $L^*ab$, where $L^* \in [0, 100]$ and $a, b \in [-128, 128]$, but $a^* = a/L^*$ and $b^* = b/L^*$.
This makes sure that changes in $L^*$ transparently result in the correct adjustment in saturation:
$a_{out} = a_{in} \cdot L^*_{out}/L^*_{in}$ and
$b_{out} = b_{in} \cdot L^*_{out}/L^*_{in}$.
Each module has to implement a \code{process} function, which is responsible for processing of three types of
pipelines: preview, full, and export. Most of the time, this can be handled transparently, i.e.\ needs no special
handling in the module. The pipelines are as follows:
\begin{description}
\item[preview] all of the image, but downsampled to low resolution for the navigation view, approximate full image statistics, and fast scrolling.
\item[full] full resolution, but only the part that can currently be seen (the region of interest, roi, will be adjusted accordingly).
\item[export] full image at full resolution, no gui data is inited for the module.
\end{description}
and can be identified by the pipeline struct, which carries a \code{pipe->type} flag. This struct can in turn be found
via the pipeline data piece which is passed to \code{process}: \code{piece->pipe}.
\paragraph{Regions of Interest}
A region of interest holds $x,y,w,h$, and scale information. The semantics are as follows: multiply the whole resolution image by
the scale value, then crop it to the window defined by $x, y, w, h$. The passed input buffer will be a contiguous block of values as defined by
the input color space, pixel by pixel and row by row. The output buffer should be in the same format, but the extent should match the
output region of interest.
\subsubsection{Most important Gui Callbacks}
\begin{description}
\item \code{gui\_init} gui thread, init the widget (not called for export)
\item \code{gui\_update} gui thread, make the gui match the modules parameters (not called for export)
\item \code{commit\_params} core thread + gui thread in synch, copy module->params to module->data
\item \code{process} core thread, execute the op on the input buffer
\item \code{modify\_roi\_in}
\item \code{modify\_roi\_out} core thread, optional, only needed if input and output buffers are of different size.
For examples, see \code{src/iop/lens.c} and \code{src/iop/clipping.c}.
\end{description}
\subsubsection{Data Structures}
\begin{description}
\item[\code{dt\_iop\_*\_params\_t module->\{params, default\_params, factory\_params\}}]
This struct should only be modified by the gui thread. The three fields exist only once per module: \code{params}
store the currently active parameters, \code{default\_params} are the default parameters (possibly adjusted by the
user), and \code{factory\_params} are dt's factory defaults (automatically copied by the core after \code{init} has been
called).
Also, don't store pointers to additional memory in this struct. It will be
stored in the database as blob and copied byte-wise as you defined it. This
means that your pointer will point to garbage memory after leaving/re-entering
darkroom mode, or during export.
\item[\code{dt\_iop\_*\_data\_t piece->data}]
This struct lives in pixel pipeline domain, and thus exists once for the full and once for the preview pipeline.
It should only be modified by the core thread (which calls \code{commit\_params} and \code{process}).
\item[\code{dt\_iop\_*\_gui\_data\_t module->gui\_data}]
You can use this struct to store your gui data (from the gui thread, obviously). It exists once per module and should be inited in \code{gui\_init} and
cleaned up in \code{gui\_cleanup}. Note that gtk+ widgets are cleaned up implicitly while the widget is destroyed.
\item[\code{dt\_iop\_*\_global\_data\_t module->data}]
Core thread global data for all pipes (such as lensfun description database).
\end{description}
\subsubsection{A most useless Example}
\todo{script to generate as up-to-date minimal dt header set to compile external plugins}
\todo{a silly template module line by line: render a checker board on top of your input image.}
See \code{src/iop/useless.c}.
Change \code{src/iop/Makefile.am}:
\begin{verbatim}
libuseless_la_SOURCES=useless.c
dtplugins_LTLIBRARIES=libtonecurve.la [..] libcolortransfer.la libuseless.la
\end{verbatim}
\end{document}
|