# Desarrollo de extensiones para &krita;

## Introducción

&krita; se puede extender infinitamente con extensiones. Las herramientas, los filtros y muchas partes de la interfaz de usuario, e incluso los espacios de colores son extensiones. De hecho, &krita; reconoce seis tipos de extensiones:

- espacios de colores: definen los canales que constituyen un solo píxel

- herramientas: cualquier cosa que se pueda hacer con un ratón o una tableta digitalizadora

- operaciones de pintura: herramientas para aplicar efectos a la pintura

- filtros de imágenes: cambio de todos los píxeles o sólo los seleccionados de una capa.

- extensiones de vista: extienden la interfaz de usuario de Krita con diálogos, paletas y operaciones.

- filtros de importación/exportación: lectura y escritura de todos los tipos de formatos de imágenes.

&krita; consta de tres bibliotecas en capas y un directorio con algunas clases que le dan soporte: kritacolor, kritaimage y kritaui. Los objetos de &krita; se pueden identificar con un `KisID`, que está compuesto de una cadena única no traducida (utilizada, por ejemplo, al guardar) y una cadena traducida para cuestiones de la GUI.

Acerca de la compatibilidad: &krita; está en desarrollo. Desde &krita; 1.5 hasta 1.6 no se han realizado tantos cambios en la API como se esperaban, pero hay algunos. De &krita; 1.6 a 2.0 haremos la transición de &Qt;3 a &Qt;4, de &kde;3 a &kde;4, de `automake` a `cmake`: se esperan demasiados cambios. Si desarrolla una extensión para &krita; y quiere hacerlo en el repositorio subversion de &krita;, hay excelentes oportunidades para recibir nuestra ayuda en la migración. Estos cambios también pueden convertir en obsoleta parte de esta documentación. Compruebe siempre la documentación más reciente de la API o los archivos de cabecera instalados en su sistema.

### KritaColor

La primera biblioteca es kritacolor. Esta biblioteca carga las extensiones del espacio de colores.

Una extensión del espacio de colores debe implementar la clase abstracta `KisColorSpace` o, si las capacidades básicas del nuevo espacio de colores las implementa `lcms` (), extender `KisAbstractColorSpace`. La biblioteca kritacolor se puede utilizar desde otras aplicaciones y no dependen de &koffice;.

### KritaImage

La biblioteca libkritaimage carga el filtro y las extensiones de operaciones de pintura y es la responsable de cargar los datos de la imagen: cambiar píxeles, componer y pintar. Las brochas, las paletas, los degradados y los patrones también los carga libkritaimage. El objetivo último es hacer que libkritaimage sea independiente de &koffice;, pero ahora compartimos el código para cargar degradados con &koffice;.

Por ahora no es fácil añadir a &krita; tipos de recursos nuevos como brochas, paletas, degradados o patrones. (Añadir brochas, paletas, degradados y patrones nuevos, sin embargos, es fácil). Para ello, &krita; sigue las líneas maestras de Crear proyecto (). Añadir soporte para archivos de brochas de Photoshop precisa de algo de trabajo en libkritaimage, pero añadir más archivos de datos de brochas de gimp no.

`KritaImage` carga los siguientes tipos de extensiones:

- Los filtros de &krita; deben extender e implementar la clase abstracta `KisFilter`, `KisFilterConfiguration` y, posiblemente, `KisFilterConfigurationWidget`. Un ejemplo de filtro es la Máscara de enfoque.

- Las operaciones de pintura (o paintops) son el conjunto de herramientas para realizar operaciones de pintura como trazos a mano alzada o círculos al que tenga acceso. Ejemplos de paintops son la pluma, el aerógrafo y la goma de borrar. Las paintops deben extender a la clase base `KisPaintop`. Ejemplos de nuevas paintops son una brocha de tiza, una brocha de pintura al óleo o una brocha programable compleja.

### KritaUI

La biblioteca libkritaui carga las herramientas y las extensiones de vista. Esta biblioteca es una parte de &koffice;, pero también contiene varios widgets que son útiles para las aplicaciones gráficas. Quizás tengamos que dividir esta biblioteca en kritapart y kritaui en la versión 2.0. Por ahora, los programadores de guiones no tienen acceso a esta biblioteca y los programadores de extensiones sólo pueden utilizar esta biblioteca para escribir herramientas o extensiones de vista. `KritaUI` carga los siguientes tipos de extensiones:

- Las herramientas derivan de `KisTool` o una de las clases base de herramientas especializadas, como `KisToolPaint`, `KisToolNonPaint` o `KisToolFreehand`. Una herramienta nueva puede ser una herramienta de selección de objetos en primer plano. Las herramientas de pintura (entre las que se incluye la pintura en la selección) puede utilizar cualquier paintop para determinar la forma de modificar los píxeles.

- Las extensiones de vista son KParts normales que utilizan `kxmlgui` para insinuarse a la interfaz de usuario de &krita;. Opciones de menú, diálogos, barras de herramientas: cualquier tipo de extensión de la interfaz de usuario puede ser una extensión de vista. De hecho, la funcionalidad importante como el soporte para guiones de &krita; se escribe como una extensión de vista.

### Filtros de importación/exportación

Los filtros de importación/exportación son filtros de &koffice;, subclases de`KoFilter`. Los filtros de lectura y de escritura de datos de imágenes de cualquiera de la multitud de formatos de imagen que existen. Y como ejemplo de nuevo filtro de importación/exportación de &krita;, podría ser un filtro para PDF. Los filtros los cargan las bibliotecas de &koffice;.

## Crear extensiones

Las extensiones se escriben en C++ y se pueden utilizar todas las API de desarrollo de &kde;, &Qt; y &krita;. Sólo se deben usar las extensiones de vista de la API de &koffice;. No se preocupe: las APIs de &krita; son bastante claras y exhaustivamente documentadas (considerando que son software libre) y codificar su primer filtro es realmente fácil.

Si no quiere utilizar C++, puede escribir guiones en Python o en Ruby: es algo completamente diferente, y por ahora no puede escribir ni herramientas ni espacios de colores ni paintops ni filtros de importación/exportación como guiones.

Las extensiones de &krita; utilizan el mecanismo de partes de &kde; para cargar, por lo que aquí también es relevante la documentación de .

Su distribución debería tener instalados los archivos de cabecera relevantes para &krita;, o quizás tenga dividido el paquete y los archivos de cabecera residan en algún paquete &koffice; dev o a &krita; dev. Puede encontrar la documentación para la API pública de &krita; en .

### Automake (y CMake)

&kde; 3.x y, por tanto, &koffice; 1.5 y 1.6 emplean `automake`. &kde; 4.0 y &koffice; 2.0 emplean `cmake`. Este tutorial describe la forma de crear extensiones con `automake`.

Las extensiones son módulos de &kde; y como tales se deben etiquetas en sus `Makefile.am`. Los filtros, las herramientas, las operaciones de pintura, los espacios de colores y los filtros de importación/exportación necesitan los archivos`.desktop`. Las extensiones de vista necesitan además un archivo `pluginname.rc` de KXMLGui. La forma más fácil de comenzar es obtener el proyecto krita-plugins del repositorio Subversion de &koffice; y usarlo como base para su propio proyecto. Queremos preparar un paquete de esqueletos para &krita; para KDevelop, pero aún no hemos tenido tiempo de ponernos a trabajar en ello.

#### `Makefile.am`

Echemos un vistazo al esqueleto de un módulo de extensión. Primero, el archivo `Makefile.am`. Es lo que utiliza &kde; para generar el makefile que construye la extensión:

    kde_services_DATA = kritaLIBRARYNAME.desktop

    INCLUDES = $(all_includes)

    kritaLIBRARYNAME_la_SOURCES = sourcefile1.cc sourcefile2.cc

    kde_module_LTLIBRARIES = kritaLIBRARYNAME.la
    noinst_HEADERS = header1.h header2.h

    kritaLIBRARYNAME_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
    kritaLIBRARY_la_LIBADD = -lkritacommon

    kritaextensioncolorsfilters_la_METASOURCES = AUTO

Este es el makefile para una extensión de filtro. Reemplace LIBRARYNAME por el nombre de su extensión, y ya está.

Si se trata de una extensión de vista, posiblemente quiera instalar también un archivo `.rc` con entradas para barras de menú y barras de herramientas. También puede querer instalar cursores e iconos. Para ello, puede formular algunos encantamientos en el archivo `Makefile.am` de &kde;:

    kritarcdir = $(kde_datadir)/krita/kritaplugins
    kritarc_DATA = LIBRARYNAME.rc
    EXTRA_DIST = $(kritarc_DATA)

    kritapics_DATA = \
       bla.png \
       bla_cursor.png
    kritapicsdir = $(kde_datadir)/krita/pics

#### Archivos Desktop

El archivo `.desktop` anuncia el tipo de extensión:

    [Desktop Entry]
    Encoding=UTF-8
    Icon=
    Name=User-visible Name
    ServiceTypes=Krita/Filter
    Type=Service
    X-KDE-Library=kritaLIBRARYNAME
    X-KDE-Version=2

Los tipos de servicios posibles son:

- Krita/Filter

- Krita/Paintop

- Krita/ViewPlugin

- Krita/Tool

- Krita/ColorSpace

Los filtros de importación y exportación de archivos utilizan el entorno de filtros genérico de &koffice; y se tratar por separado.

#### Boilerplate

También necesita un poco de código bilerplate que se llama desde el entorno de la parte de &kde; para instanciar la extensión: un archivo de cabecera y un archivo de implementación.

Un archivo de cabecera:

    #ifndef TOOL_STAR_H_
    #define TOOL_STAR_H_

    #include <kparts/plugin.h>

    /**
    * A module that provides a star tool.
    */
    class ToolStar : public KParts::Plugin
    {
       Q_OBJECT
    public:
       ToolStar(QObject *parent, const char *name, const QStringList &);
       virtual ~ToolStar();

    };

    #endif // TOOL_STAR_H_

Y un archivo de implementación:

    #include <kinstance.h>
    #include <kgenericfactory.h>

    #include <kis_tool_registry.h>

    #include "tool_star.h"
    #include "kis_tool_star.h"

    typedef KGenericFactory<ToolStar> ToolStarFactory;
    K_EXPORT_COMPONENT_FACTORY( kritatoolstar, ToolStarFactory( "krita" ) )

    ToolStar::ToolStar(QObject *parent, const char *name, const QStringList &)
       : KParts::Plugin(parent, name)
    {
       setInstance(ToolStarFactory::instance());
       if ( parent->inherits("KisToolRegistry") )
       {
           KisToolRegistry * r = dynamic_cast<KisToolRegistry*>( parent );
           r -> add(new KisToolStarFactory());
       }

    }

    ToolStar::~ToolStar()
    {
    }

    #include "tool_star.moc"

#### Registros

Las herramientas las carga el registro de herramientas y se registran con el registro de herramientas. Las extensiones como herramientas, filtros y operaciones de pintura se cargan sólo una vez: las extensiones de vista se cargan cada vez que se crea una vista. Tenga en cuenta que, de forma general, registramos fábricas. Por ejemplo, con las herramientas se crea una instancia nueva de una herramienta para cada puntero (ratón, stylus, borrador). Y se crea una operación de pintura nueva cuando una herramienta recibe un evento de ratón pulsado.

Filtros que llaman el registro de filtros:

    if (parent->inherits("KisFilterRegistry")) {
           KisFilterRegistry * manager = dynamic_cast<KisFilterRegistry *>(parent);
           manager->add(new KisFilterInvert());
       }

Operaciones de pintura en el registro paintop:

    if ( parent->inherits("KisPaintOpRegistry") ) {
               KisPaintOpRegistry * r = dynamic_cast<KisPaintOpRegistry*>(parent);
               r -> add ( new KisSmearyOpFactory );
       }

Colorspaces, el registro del espacio de colores (con algunas complicaciones):

    if ( parent->inherits("KisColorSpaceFactoryRegistry") ) {
            KisColorSpaceFactoryRegistry * f = dynamic_cast<isColorSpaceFactoryRegistry*>(parent);

           KisProfile *defProfile = new KisProfile(cmsCreate_sRGBProfile());
           f->addProfile(defProfile);

           KisColorSpaceFactory * csFactory = new KisRgbColorSpaceFactory();
           f->add(csFactory);

           KisColorSpace * colorSpaceRGBA = new KisRgbColorSpace(f, 0);
           KisHistogramProducerFactoryRegistry::instance() -> add(
                   new KisBasicHistogramProducerFactory<KisBasicU8HistogramProducer>
                   (KisID("RGB8HISTO", i18n("RGB8 Histogram")), colorSpaceRGBA) );
       }

Las extensiones de vista no tienen que registrarse, y tienen acceso a un objeto `KisView`:

    if ( parent->inherits("KisView") )
       {
           setInstance(ShearImageFactory::instance());
           setXMLFile(locate("data","kritaplugins/shearimage.rc"), true);

           (void) new KAction(i18n("&Shear Image..."), 0, 0, this, SLOT(slotShearImage()), actionCollection(), "shearimage");
           (void) new KAction(i18n("&Shear Layer..."), 0, 0, this, SLOT(slotShearLayer()), actionCollection(), "shearlayer");

           m_view = (KisView*) parent;
       }

Recuerde que esto significa que una extensión de vista se crea para cada vista que crea el usuario: dividir una vista implica volver a cargar todos los plugins.

#### Versionado de extensiones

&krita; 1.5 carga las extensiones que tienen definido`X-KDE-Version=2` en el archivo `.desktop`. Las extensiones de &krita; 1.6 probablemente sean binariamente incompatibles con las extensiones de 1.5 y necesitan tener 3 como número de versión. Las extensiones de &krita; 2.0 necesitan el número de versión 3. Es cierto, aunque no es del todo lógico.

## Espacios de colores

Los espacios de colores implementan la clase pura virtual `KisColorSpace`. Hay dos tipos de espacios de colores: los que utilizan `lcms` para las transformaciones entre espacios de colores y los que son demasiado raros como para que los gestione `lcms`. Ejemplos de los primeros son cmyk, rgb, yuv. Un ejemplo del último es la acuarela o húmedo y pegajoso. Los espacios de colores que utilizan `lcms` se pueden derivar de `KisAbstractColorSpace`, o de una de las clases base que se especializan para algunos bits por canal.

Implementar un espacio de colores es bastante fácil. El principio general es que el espacio de colores funciona como un simple vector de bytes. La interpretación de estos bytes es la que define el espacio de colores. Por ejemplo, un píxel en GrayA de 16 bits consta de cuatro bytes: dos bytes para el valor de gris y dos bytes para el valor alfa. Puede utilizar una estructura para trabajar con la disposición de memoria de un píxel en la implementación del espacio de colores, pero esa representación no se exporta. La única forma que tiene &krita; de saber qué canales y qué tipos de canales conforman su espacio de colores es a través de la clase `KisChannelInfo`.

Los filtros y las operaciones de pintura pueden usar la extensa colección de métodos que ofrece `KisColorSpace` para hacer su trabajo. En muchas ocasiones, la implementación predefinida de `KisAbstractColorSpace` funcionará, pero más lenta que una implementación personalizada de su propio espacio de colores, porque `KisAbstractColorSpace` convierte todos los píxeles a L*a*b de 16 bits y luego lo devuelve a su formato original.

### `KisChannelInfo`

    (http://websvn.kde.org/trunk/koffice/krita/kritacolor/kis_channelinfo.h)

Esta clase define los canales que conforman un solo píxel en un espacio de colores particular. Un canal tiene las siguientes características importantes:

- un nombre para mostrarlo en la interfaz de usuario

- una posición: el byte en el que los bytes que representan este canal comienzan en el píxel.

- un tipo: color, alfa, sustancia o sustrato. El color es un color plano, alfa es la transparencia, sustancia es una representación de la cantidad de pigmento o similar, sustrato es la representación del lienzo. (Tenga en cuenta que estos valores pueden cambiar rápidamente).

- un tipo de valor: byte, short, integer, float u otro

- tamaño: el número de bytes que ocupa este canal

- color: un `QColor` que representa esta canal para la visualización de la interfaz del usuario, por ejemplo en histogramas.

- una abreviatura para utilizar en la GUI cuando no hay demasiado espacio

### `KisCompositeOp`

Al igual que el Porter-Duff original, hay muchas formas de combinar los píxeles para obtener un color nuevo. La clase `KisCompositeOp` define la mayoría de ellos: este conjunto no se puede extender fácilmente, salvo para modificar la biblioteca kritacolor.

Una extensión de espacio de colores puede admitir cualquier subconjunto posible de operaciones de composición, pero el conjunto siempre debe incluir "OVER" (igual que "NORMAL") y "COPY". Los demás son opcionales en mayor o menor grado, aunque mientras más, mejor.

### `KisColorSpace`

Los métodos de la clase pura virtual `KisColorSpace` se pueden dividir en varios grupos: conversión, identificación y manipulación.

Todas las clases deben poder convertir un píxel de y a RGB de 8 bits (por ejemplo, un `QColor`), y preferentemente también de y a L*a*b de 16 bits. También hay un método para convertir a cualquier espacio de colores el espacio de color actual.

Los espacios de colores se describen mediante el vector `KisChannelInfo`, el número de canales, el número de bytes de un solo píxel, si se admiten o no imágenes HDR y más.

La manipulación puede ser, por ejemplo, la combinación de dos píxeles en un píxel nuevo: bitBlt, oscureciendo o haciendo girar los píxeles.

Puede consultar en la documentación de la API la descripción completa de todos los métodos que necesita para implementar un espacio de colores.

`KisAbstractColorSpace` implementa muchos de los métodos virtuales de `KisColorSpace`, usando funciones de la biblioteca `lcms`. Por encima de `KisAbstractColorSpace` están las clases para espacios de colores de enteros de 8 y de 16 bits y de decimales de 16 y de 32 bits, que definen las operaciones habituales para realizar transformaciones entre diferencias de colores.

## Filtros

Los filtros son extensiones que examinan los píxeles de una capa y los modifican. Aunque &krita; utiliza unos compartimentos de memoria bastante eficientes para almacenar los píxeles, los escritores de los filtros no tienen por qué preocuparse de ella. Cuando se escribe una extensión de filtro para la API de transformaciones de imágenes de &Java;, Photoshop o The Gimp, usted se tiene que ocupar de los límites y del ensamblado de los compartimentos de memoria. En &krita; este detalle de la implementación permanece oculto.

Teóricamente, es fácil reemplazar los datos almacenados en el compartimento de la imagen por otros datos, pero ahora mismo los compartimentos no son extensiones verdaderos por razones de rendimiento.

&krita; emplea iteradores para leer y escribir los valores de los píxeles. También puede leer un bloque de píxeles con un búfer de memoria, jugar con él y luego volver a escribirlo como un bloque. Pero no tiene por qué ser más eficiente. Incluso puede ser más lenta que el uso de los iteradores. Aunque quizás sí que sea más conveniente. Más información en la documentación de la API.

En &krita;, las imágenes están compuestas de capas, que pueden ser de cuatro tipos: capas de pintura, capas de grupo, capas de ajuste (que contienen un filtro que se aplica dinámicamente a las capas que hay debajo de la capa de ajuste) y las capas de partes. Los filtros siempre pueden operar sobre las capas de pintura. Las capas de pintura contienen dispositivos de pintura, de la clase `KisPaintDevice`. Un dispositivo de pintura bien configurado da acceso a los píxeles reales.

Los dispositivos de pintura (`PaintDevice` ) se suelen pasar a los punteros compartidos. Un puntero compartido mantiene la pista de cuántos sitios está utilizando el dispositivo de pintura y elimina el dispositivo de pintura cuando ya no se utiliza. Puede reconocer la versión del puntero compartido de un dispositivo de pintura mediante el sufijo `SP`. Simplemente recuerde que nunca tiene que eliminar explícitamente un `KisPaintDeviceSP`.

Vamos a examinar un filtro muy sencillo, uno que invierte cada píxel. El código para este filtro está en el directorio `koffice/krita/plugins/filters/example`. El método principal es

    KisFilterInvert::process(KisPaintDeviceSP src, KisPaintDeviceSP dst,
                            KisFilterConfiguration* /*config*/, const QRect& rect).

A la función se le pasan dos dispositivos de pintura, un objeto de configuración (que no se utiliza en este filtro tan sencillo) y un `rect`. El `rect` describe el área del dispositivo de pintura sobre el que debe actuar el filtro. Este área se describe mediante enteros, lo que significa que no hay precisión de subpíxeles.

El dispositivo de pintura de origen (`src`) es aquel del que se leen los datos, el dispositivo de pintura de destino (`dst`) es aquel en el que se va a escribir. Estos parámetros pueden referirme al mismo dispositivo de pintura real o ser dos dispositivos de pintura diferentes. (Tenga en cuenta que en el futuro esta opción puede cambiar para que sólo haya un dispositivo de pintura).

Ahora, echemos un vistazo al código línea a línea.

    void KisFilterInvert::process(KisPaintDeviceSP src, KisPaintDeviceSP dst,
                                 KisFilterConfiguration* /*config*/, const QRect& rect)
    {
       Q_ASSERT(src != 0);
       Q_ASSERT(dst != 0);

       KisRectIteratorPixel srcIt = src->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), false);
       KisRectIteratorPixel dstIt = dst->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), true );

       int pixelsProcessed = 0;
       setProgressTotalSteps(rect.width() * rect.height());

       KisColorSpace * cs = src->colorSpace();
       Q_INT32 psize = cs->pixelSize();

       while( ! srcIt.isDone() )
       {
           if(srcIt.isSelected())
           {
               memcpy(dstIt.rawData(), srcIt.oldRawData(), psize);

               cs->invertColor( dstIt.rawData(), 1);
           }
           setProgress(++pixelsProcessed);
           ++srcIt;
           ++dstIt;
       }
       setProgressDone(); // Must be called even if you don't really support progression
    }

Crea un iterador para leer los píxeles existentes. Krita tiene tres tipos de iteradores: horizontal, vertical y rectangular. El iteradore rect recibe la ruta más eficiente para los datos de la imagen, pero no garantiza nada de la ubicación del píxel siguiente que devuelve. Eso significa que no puede asegurarse de que el próximo píxel que se va a recuparea sea adyacente al que acaba de recibir. Los iteradores de líneas en horizontal y en vertical pueden garantizar la ubicación de los píxeles que devuelven.

(2) Creamos el iterador de destino con la opción de escribir (`write`) a cierto (`true`). Esto significa que si el dispositivo de pintura de destino es mejor que el rectángulo que se va a escribir, se aumentará de tamaño para que sea válida el área para todos los píxeles sobre los que se itera. Tenga en cuenta que aquí tenemos un error potencial: si `dst` y `src` no son el mismo dispositivo, es bastante probable que los píxeles que devuelven los iteradores no se corresponden. Para cada posición del iterador, `src` puede estar, por ejemplo, en 165,200, mientras que `dst` puede estar en 20,8. En este caso, la copia que realizamos abajo puede distorsionar la imagen...

¿Quiere saber si un píxel está seleccionado? Pues es muy fácil. Utilice el método `isSelected`. Pero la selección no es una propiedad binaria del píxel, porque un píxel puede estar medio seleccionado, poco seleccionado o seleccionado casi por completo. Ese valor lo puede obtener del iterador. Las selecciones son realmente una máscara del dispositivo de pintura con un rango entre 0 y 255, siendo 0 completamente deseleccionado y 255 completamente seleccionado. El iterador tiene dos métodos: `isSelected()` y`selectedNess()`. El primero devuelve cierto si un píxel está seleccionado en algún grado (esto es, si el valor de la máscar es mayor que 1), el otro devuelve el valor de la máscara.

Como ya se indicó, `memcpy` es un error considerable... `rawData()` devuelve el vector de bytes que define el estado real del píxel. `oldRawData()` devuelve el vector de bytes tal y como estaba antes de crear el iterador. Sin embargo, podemos copiar aquí el píxel incorrecto. En la práctica real, no es demasiado frecuente que suceda esto, a no ser que `dst` ya exista y no esté alineada con `src`.

Pero esto es correcto: en lugar de averiguar qué bytes representa ese canal, se utiliza una función que suministran todos los espacios de colores para invertir el píxel actual. El espacio de colores tiene un montón de operaciones de píxeles que puede utilizar.

Para crear un filtro, hay algunas otras opciones. Los filtros tienen otros dos componentes importantes: un objeto de configuración y un widget de configuración. Ambos están muy relacionados e interactúan. El widget de configuración crea un objeto de configuración, pero también se puede rellenar con la configuración de un objeto existente. Los objetos de configuración se pueden representar como XML y se pueden crear desde XML. Es lo que hacen posible las capas de ajuste.

### Iteradores

Hay tres tipos de iteradores:

- Líneas horizontales

- Líneas verticales

- Iteradores rectangulares

Los iteradores de líneas horizontales y verticales tienen un método para mover el iterador hacia la siguiente fila o columna: `nextRow()` y `nextCol()`. Usar estas funciones es mucho más rápido que crear un iterador nuevo para cada línea o columna.

Los iteradores se pueden ubicar en hilos en &krita;, lo que significa que se puede repartir el trabajo entre varios hilos. Sin embargo, las futuras versiones de &krita; utilizarán el método `supportsThreading()` para determinar si su filtro se puede aplicar a partes de la imagen (&ie;, todos los píxeles modificados de forma independiente, en lugar de los modificados por algunos valores determinados por el examen de todos los píxeles de la imagen) y crear automáticamente el hilo de ejecución del filtro.

### `KisFilterConfiguration`

`KisFilterConfiguration` es una estructura que se utiliza para guardar en disco las opciones del filtro, por ejemplo para las capas de ajuste. Esta extensión de guiones utiliza el mapa de propiedades como soporte de `KisFilterConfigaration` para poder escribir guiones como filtros. Los filtros pueden proporcionar un widget personalizado para cuando los muestre &krita; en la galería de filtros, en la pestaña de vista previa del filtro o en la pestaña de opciones de la herramienta del filtro para los filtros de pintura.

Un ejemplo, tomado del filtro de efecto de pintura al óleo:

    class KisOilPaintFilterConfiguration : public KisFilterConfiguration
    {

    public:

       KisOilPaintFilterConfiguration(Q_UINT32 brushSize, Q_UINT32 smooth)
           : KisFilterConfiguration( "oilpaint", 1 )
           {
               setProperty("brushSize", brushSize);
               setProperty("smooth", smooth);
           };
    public:

       inline Q_UINT32 brushSize() { return getInt("brushSize"); };
       inline Q_UINT32 smooth() {return getInt("smooth"); };

    };

### `KisFilterConfigurationWidget`

El usuario puede ajustar casi todos los filtros. Puede crear un widget de configuración, que Krita usará cuando se emplee el filtro. Un ejemplo:

El diálogo Pintura al óleo El diálogo Pintura al óleo

El diálogo Pintura al óleo

Tenga en cuenta que la zona izquierda es la única bajo su responsabilidad: &krita; se ocupa del resto. Hay tres formas de crear un widget de opciones:

- Utilizar &Qt; Designer para crear un widget base, y una subclase de él para un filtro

- Usar uno de los widgets individuales que muestran varios deslizadores para listas de enteros, decimales de doble precisión o booleanos. Son útiles, como en el caso de la captura de pantalla de arriba, si el filtro se puede configurar mediante enteros, decimales de precisión dobles o booleanos. Puede ver la documentación de la API de `KisMultiIntegerFilterWidget`, `KisMultiDoubleFilterWidget` y `KisMultiBoolFilterWidget`.

- Codificar el widget a mano. No se recomienda, ya que de hacerlo así su filtro se convierte en parte de la versión oficial de &krita;. Por ello, se le ruega que replantee la codificación manual del widget y que realice su diseño con &Qt; Designer.

El filtro de pintura al óleo utiliza un widget con varios enteros:

    KisFilterConfigWidget * KisOilPaintFilter::createConfigurationWidget(QWidget* parent, KisPaintDeviceSP /*dev*/)
    {
       vKisIntegerWidgetParam param;
       param.push_back( KisIntegerWidgetParam( 1, 5, 1, i18n("Brush size"), "brushSize" ) );
       param.push_back( KisIntegerWidgetParam( 10, 255, 30, i18n("Smooth"), "smooth" ) );
       return new KisMultiIntegerFilterWidget(parent, id().id().ascii(), id().id().ascii(), param );
    }

    KisFilterConfiguration* KisOilPaintFilter::configuration(QWidget* nwidget)
    {
       KisMultiIntegerFilterWidget* widget = (KisMultiIntegerFilterWidget*) nwidget;
       if( widget == 0 )
       {
           return new KisOilPaintFilterConfiguration( 1, 30);
       } else {
           return new KisOilPaintFilterConfiguration( widget->valueAt( 0 ), widget->valueAt( 1 ) );
       }
    }

    std::list<KisFilterConfiguration*> KisOilPaintFilter::listOfExamplesConfiguration(KisPaintDeviceSP )
    {
       std::list<KisFilterConfiguration*> list;
       list.insert(list.begin(), new KisOilPaintFilterConfiguration( 1, 30));
       return list;
    }

Veamos cómo trabaja: rellene un vector con sus parámetros enteros y cree el widget. El método `configuration()` inspecciona el widget y crea el objeto correctode configuración del filtro, en este caso, por supuesto, `KisOilPaintFilterConfiguration`. El método `listOfExamplesConfiguration` (que debería renombrarse a un inglés correcto...) devuelve una lista con los objetos de configuración del ejemplo para el diálogo de la galería de los filtros.

### Conclusión de los filtros

Existen filtros más interesantes para codificar, por supuesto, pero con esta explicación, la documentación de la API y el acceso a nuestro código fuente, debería ser suficiente para comenzar. No dude en contactar con los desarrolladores de &krita; en el IRC o en las listas de correo.

## Herramientas

Las herramientas aparecen en el cuadro de herramientas de &krita;. Esto significa que hay espacio limitado para nuevas herramientas (piense con cuidado si una operación de dibujo no es suficiente para sus propósitos). Las herramientas pueden utilizar el ratón/tableta y el teclado de formas complejas, que no pueden realizar las operaciones de dibujo. Esta es la razón de que Duplicar sea una herramienta y aerosol una operación de dibujo.

Tenga cuidado con los datos estáticos de su herramienta: se creará una nueva instancia de su herramienta para cada dispositivo de entrada: ratón, puntero, borrador, aerosol (cualquiera). Las herramientas se dividen en grupos lógicos:

- herramientas para dibujar formas (círculo, rectángulo)

- herramientas para dibujar sin manos (brocha)

- herramientas para desordenar la geometría de un capa

- herramientas de relleno (como relleno cúbico o gradiente)

- herramientas de vista (no cambian los pixels, sino que alteran la forma de ver el lienzo, como la ampliación)

- herramientas de selección (que cambia la máscara de selección)

La interfaz de la herramienta se describe en la documentación de la API para `KisTool`. Existen tres subclases: `KisToolPaint`, `KisToolNonPaint` y `KisToolShape` (que en estos momentos es una subclase de `KisToolPaint`) que especializa `KisTool` para tareas de dibujo (es decir, cambio de pixels), tareas que no son de dibujo y tareas de dibujado de formas.

Una herramienta tiene widget de opción, al igual que los filtros. Los widget de opciones se muestran en una pestaña en una ventana empotrada. Podemos cambiar esto por una tira bajo el menú principal (que reemplaza a la barra de herramientas) para &krita; 2.0, pero por ahora, diseñe el widget de sus opciones para ajustarse a una pestaña. Como siempre, lo mejor es utilizar el diseñador &Qt; para diseñar el widget de la opción.

Un buen ejemplo de una herramienta es la barra de estrella:

    kis_tool_star.cc       Makefile.am   tool_star_cursor.png  wdg_tool_star.ui
    kis_tool_star.h        Makefile.in   tool_star.h
    kritatoolstar.desktop  tool_star.cc  tool_star.png

Como ve, necesita dos imágenes: una para el cursor y otra para la barra de herramientas. `tool_star.cc` es el cargador de la extensión, similar a la que pudimos ver anteriormente. Lo importante se encuentra en la implementación:

    KisToolStar::KisToolStar()
       : KisToolShape(i18n("Star")),
         m_dragging (false),
         m_currentImage (0)
    {
       setName("tool_star");
       setCursor(KisCursor::load("tool_star_cursor.png", 6, 6));
       m_innerOuterRatio=40;
       m_vertices=5;
    }

El constructor establece el nombre interno —que no se traduce— y la llamada a la superclase establece el nombre visible. También cargaremos la imagen del cursor y estableceremos un número de variables.

    void KisToolStar::update (KisCanvasSubject *subject)
    {
       KisToolShape::update (subject);
       if (m_subject)
           m_currentImage = m_subject->currentImg();
    }

El método `update()` se llama cuando la herramienta se selecciona. Éste no es un método de `KisTool`, sino un método de `KisCanvasObserver`. El observador del lienzo recibe una notificación cuando se produce algún cambio en la vista, que puede ser práctico para las herramientas.

Los siguientes métodos (`buttonPress`, `move` y `buttonRelease`) los llama &krita; cuando el dispositivo de entrada (ratón, puntero, borrador, etc.) se pulsa, se mueve o se suelta. Tenga en cuenta que puede mover eventos si el botón del ratón no está pulsado. Los eventos no son eventos habituales de &Qt;, sino eventos sintéticos de &krita; porque hacemos uso de trucos de bajo nivel para obtener suficientes eventos para dibujar una línea suave. De forma predefinida, los grupos de herramientas como &Qt; (y GTK) sueltan eventos si están demasiado ocupadas para manejarlos, y los necesitamos todos.

    void KisToolStar::buttonPress(KisButtonPressEvent *event)
    {
       if (m_currentImage && event->button() == LeftButton) {
           m_dragging = true;
           m_dragStart = event->pos();
           m_dragEnd = event->pos();
           m_vertices = m_optWidget->verticesSpinBox->value();
           m_innerOuterRatio = m_optWidget->ratioSpinBox->value();
       }
    }

    void KisToolStar::move(KisMoveEvent *event)
    {
       if (m_dragging) {
           // borra las líneas antiguas del lienzo
           draw(m_dragStart, m_dragEnd);
           // mueve (alt) o redimensiona la estrella
           if (event->state() & Qt::AltButton) {
               KisPoint trans = event->pos() - m_dragEnd;
               m_dragStart += trans;
               m_dragEnd += trans;
           } else {
               m_dragEnd = event->pos();
           }
           // dibuja nuevas líneas en el lienzo
           draw(m_dragStart, m_dragEnd);
       }
    }

    void KisToolStar::buttonRelease(KisButtonReleaseEvent *event)
    {
       if (!m_subject || !m_currentImage)
           return;

       if (m_dragging && event->button() == LeftButton) {
           // borra las líneas antiguas en el lienzo
           draw(m_dragStart, m_dragEnd);
           m_dragging = false;

           if (m_dragStart == m_dragEnd)
               return;

           if (!m_currentImage)
               return;

           if (!m_currentImage->activeDevice())
               return;

           KisPaintDeviceSP device = m_currentImage->activeDevice ();;
           KisPainter painter (device);
           if (m_currentImage->undo()) painter.beginTransaction (i18n("Star"));

           painter.setPaintColor(m_subject->fgColor());
           painter.setBackgroundColor(m_subject->bgColor());
           painter.setFillStyle(fillStyle());
           painter.setBrush(m_subject->currentBrush());
           painter.setPattern(m_subject->currentPattern());
           painter.setOpacity(m_opacity);
           painter.setCompositeOp(m_compositeOp);
           KisPaintOp * op =
               KisPaintOpRegistry::instance()->paintOp(m_subject->currentPaintop(), m_subject->currentPaintopSettings(), &painter);
           painter.setPaintOp(op); // Painter takes ownership

           vKisPoint coord = starCoordinates(m_vertices, m_dragStart.x(), m_dragStart.y(), m_dragEnd.x(), m_dragEnd.y());

           painter.paintPolygon(coord);

           device->setDirty( painter.dirtyRect() );
           notifyModified();

           if (m_currentImage->undo()) {
               m_currentImage->undoAdapter()->addCommand(painter.endTransaction());
           }
       }
    }

El método `draw()` es un método interno de `KisToolStar` y dibuja el contorno de la estrella. Se llamará desde el método `move()` para dar al usuario información del tamaño y la forma de su estrella. Observe que se utiliza la operación de tramas `Qt::NotROP`, lo que significa llamar a `draw()` por segunda vez con los mismos puntos de partida y final para dibujar la estrella que se borró.

    void KisToolStar::draw(const KisPoint& start, const KisPoint& end )
    {
       if (!m_subject || !m_currentImage)
           return;

       KisCanvasController *controller = m_subject->canvasController();
       KisCanvas *canvas = controller->kiscanvas();
       KisCanvasPainter p (canvas);
       QPen pen(Qt::SolidLine);

       KisPoint startPos;
       KisPoint endPos;
       startPos = controller->windowToView(start);
       endPos = controller->windowToView(end);

       p.setRasterOp(Qt::NotROP);

       vKisPoint points = starCoordinates(m_vertices, startPos.x(), startPos.y(), endPos.x(), endPos.y());

       for (uint i = 0; i < points.count() - 1; i++) {
           p.drawLine(points[i].floorQPoint(), points[i + 1].floorQPoint());
       }
       p.drawLine(points[points.count() - 1].floorQPoint(), points[0].floorQPoint());

       p.end ();
    }

El método `setup()` es esencial: aquí crearemos la acción que se conectará a la barra de herramientas para que los usuarios puedan seleccionar la herramienta. También asociaremos una tecla de acceso rápido. Tenga en cuenta también que se creará una instancia de la herramienta para cada dispositivo de entrada. Esto también significa que llamaremos a `setup()` para cada dispositivo de entrada, lo que implica que se añadirá varias veces una acción con el mismo nombre a la colección de acciones. Sin embargo, todo parece funcionar, entonces ¿por qué preocuparse?

    void KisToolStar::setup(KActionCollection *collection)
    {
       m_action = static_cast<KRadioAction *>(collection->action(name()));

       if (m_action == 0) {
           KShortcut shortcut(Qt::Key_Plus);
           shortcut.append(KShortcut(Qt::Key_F9));
           m_action = new KRadioAction(i18n("&Star"),
                                       "tool_star",
                                       shortcut,
                                       this,
                                       SLOT(activate()),
                                       collection,
                                       name());
           Q_CHECK_PTR(m_action);

           m_action->setToolTip(i18n("Draw a star"));
           m_action->setExclusiveGroup("tools");
           m_ownAction = true;
       }
    }

El método `starCoordinates()` contiene algunos métodos matemáticos curiosos (aunque no son interesantes para la discusión sobre cómo crear una herramienta).

    KisPoint KisToolStar::starCoordinates(int N, double mx, double my, double x, double y)
    {
       double R=0, r=0;
       Q_INT32 n=0;
       double angle;

       vKisPoint starCoordinatesArray(2*N);

       // the radius of the outer edges
       R=sqrt((x-mx)*(x-mx)+(y-my)*(y-my));

       // el radio de los bordes internos
       r=R*m_innerOuterRatio/100.0;

       // el ángulo
       angle=-atan2((x-mx),(y-my));

       //establece los bordes externos
       for(n=0;n<N;n++){
           starCoordinatesArray[2*n] = KisPoint(mx+R*cos(n * 2.0 * M_PI / N + angle),my+R*sin(n *2.0 * M_PI / N+angle));
       }

       //establece los bordes internos
       for(n=0;n<N;n++){
           starCoordinatesArray[2*n+1] = KisPoint(mx+r*cos((n + 0.5) * 2.0 * M_PI / N + angle),my+r*sin((n +0.5) * 2.0 * M_PI / N + angle));
       }

       return starCoordinatesArray;
    }

El método `createOptionWidget()` se llama para crear el widget de la opción que &krita; mostrará en la pestaña. Como es una herramienta para un dispositivo de entrada para la vista, el estado de una herramienta puede mantenerse en la herramienta. Este método solo se llama una vez: el widget de la opción se guarda y se recupera la siguiente vez que se active laherramienta.

    QWidget* KisToolStar::createOptionWidget(QWidget* parent)
    {
       QWidget *widget = KisToolShape::createOptionWidget(parent);

       m_optWidget = new WdgToolStar(widget);
       Q_CHECK_PTR(m_optWidget);

       m_optWidget->ratioSpinBox->setValue(m_innerOuterRatio);

       QGridLayout *optionLayout = new QGridLayout(widget, 1, 1);
       super::addOptionWidgetLayout(optionLayout);

       optionLayout->addWidget(m_optWidget, 0, 0);

       return widget;
    }

### Conclusiones de la herramienta

Las herramientas son extensiones relativamente fáciles de crear. Necesitará combinar las interfaces `KisTool` y `KisCanvasObserver` para crear una herramienta eficaz.

## Operaciones de dibujo

PaintsOps es uno de los tipos más innovadores de las extensiones de Krita (junto con los espacios de color extensibles). Una operación de dibujo define cómo cambian las herramientas los pixels que tocan. El arosol, también conocido como aerógrafo: éstas son todas las operaciones de dibujo. Pero podría (con mucho trabajo) crear una operación de dibujo que lea las definiciones XML de brocha de Corel Painter y utilizar estos para determinar cómo ser hará el dibujado.

Las operaciones de dibujo se instancian cuando una herramienta de dibujo recibe un evento `mouseDown` y se borra cuando se recibe un evento `mouseUp`. Entre ellos, la operación de dibujo puede mantener la pista de las posiciones previas y otros datos, como niveles de presión si el usuario está utilizando una tableta.

La operación básica de una operación de dibujo es cambiar los pixels en la posición del cursor de una herramienta de dibujo. Ésto puede hacerse solo una vez, o la operación de dibujo puede pedir que se ejecute a intervalos regulares, utilizando un temporizador. El primero puede ser práctico para una operación de dibujo del tipo lápiz, el segundo, por supuesto, para una operación de dibujo del tipo aerosol.

Las operaciones de dibujo pueden tener un pequeño widget que se sitúa en una barra de herramientas. Así, los widgets de configuración de la operación de dibujo necesitan tener una disposición horizontal de los widgets, que no pueden ser más grandes que un botón de una barra de herramientas. Aparte de eso, &krita; tiene un aspecto curioso.

Eche un vistazo a una extensión de operación de dibujo simple, una que muestra un poquito de inteligencia programática. Primero, en la cabecera del archivo, se define una fábrica. Esta fábrica crea una operación de dibujo cuando la herramienta activa necesita una:

    public:
       KisSmearyOpFactory() {}
       virtual ~KisSmearyOpFactory() {}

       virtual KisPaintOp * createOp(const KisPaintOpSettings *settings, KisPainter * painter);
       virtual KisID id() { return KisID("paintSmeary", i18n("Smeary Brush")); }
       virtual bool userVisible(KisColorSpace * ) { return false; }
       virtual QString pixmap() { return ""; }

    };

La fábrica también contiene el `KisID` con los nombres publico y privado de la operación de dibujo —asegúrese de que el nombre privado de la operación de dibujo no coincide con el de otra operación de dibujo— y opcionalmente puede devolver un mapa de pixels. &krita; puede mostrar una imagen junto con el nombre para realizar una identificación visual de su operación de dibujo. Por ejemplo, una operación de dibujo de paleta de pintor podría tener la imagen de esta como una implementación.

La implementación de la operación de dibujo es muy sencilla:

    KisSmearyOp::KisSmearyOp(KisPainter * painter)
       : KisPaintOp(painter)
    {
    }

    KisSmearyOp::~KisSmearyOp()
    {
    }
    void KisSmearyOp::paintAt(const KisPoint &pos, const KisPaintInformation& info)
    {

El método `paintAt()` es el que realmente hace el trabajo con las operaciones de dibujo. Este método recibe dos parámetros: la posición actual (que está en coma flotante, no en pixels completos) y un objeto `KisPaintInformation`, que contiene la presión, la inclinación x e y, y el vector de movimiento, y en el futuro puede extenderse con más información.

    if (!m_painter->device()) return;

       KisBrush *brush = m_painter->brush();

Un `KisBrush` es la representación de un archivo de brocha de Gimp: es una máscara, y puede ser una máscara simple o una serie de máscaras. En realidad, no utilizaremos la brocha aquí, excepto para determinar el «punto» bajo el cursor.

    Q_ASSERT(brush);

       if (!brush) return;

       if (! brush->canPaintFor(info) )
           return;

       KisPaintDeviceSP device = m_painter->device();
       KisColorSpace * colorSpace = device->colorSpace();
       KisColor kc = m_painter->paintColor();
       kc.convertTo(colorSpace);

       KisPoint hotSpot = brush->hotSpot(info);
       KisPoint pt = pos - hotSpot;

       // Divide las coordenadas en enteros más partes fraccionarias. El entero
       // es el lugar en el que se posicionará la pincelada y la parte fraccionaria determina
       // el posicionamiento del sub-pixel.
       Q_INT32 x, y;
       double xFraction, yFraction;

       splitCoordinate(pt.x(), &x, &xFraction);
       splitCoordinate(pt.y(), &y, &yFraction);

       KisPaintDeviceSP dab = new KisPaintDevice(colorSpace, "pincelada");
       Q_CHECK_PTR(dab);

No cambiaremos los pixels de un dispositivo de dibujo directamente: en lugar de esto crearemos un pequeño dispositivo de dibujo, una pincelada, y componerlo en el dispositivo de dibujo actual.

    m_painter->setPressure(info.pressure);

Como indica el comentario, el siguiente código de bit hace algo de trabajo programático para crear la pincelada actual. Cuando se hace con esta operación de dibujo, la longitud, posición y grosor de las líneas dependerá de la presión y de la carga de pintura, y crearemos una pincelada al óleo con una brocha dura. Pero todavía no he tenido tiempo de terminar esto.

    // Calcula la posición del mechón. Los trazos se colocan en una línea
       // perpendicular al movimiento de la brocha, es decir, la línea recta entre
       // la posición actual y la posición anterior.
       // Los trazos se dispersan de acuerdo con la presión

       KisPoint previousPoint = info.movement.toKisPoint();
       KisVector2D brushVector(-previousPoint.y(), previousPoint.x());
       KisVector2D currentPointVector = KisVector2D(pos);
       brushVector.normalize();

       KisVector2D vl, vr;

       for (int i = 0; i < (NUMBER_OF_TUFTS / 2); ++i) {
           // Calcula las posiciones del nuevo vector.
           vl = currentPointVector + i * brushVector;
           KisPoint pl = vl.toKisPoint();
           dab->setPixel(pl.roundX(), pl.roundY(), kc);

           vr = currentPointVector - i * brushVector;
           KisPoint pr = vr.toKisPoint();
           dab->setPixel(pr.roundX(), pr.roundY(), kc);
       }

       vr = vr - vl;
       vr.normalize();

Finalmente el diseño de la pincelada en el dispositivo de dibujo original e indicarle que al pintor que se actualiza un pequeño rectángulo en el dispositivo de dibujo.

    if (m_source->hasSelection()) {
           m_painter->bltSelection(x - 32, y - 32, m_painter->compositeOp(), dab.data(),
                                   m_source->selection(), m_painter->opacity(), x - 32, y -32, 64, 64);
       }
       else {
           m_painter->bitBlt(x - 32, y - 32, m_painter->compositeOp(), dab.data(), m_painter->opacity(), x - 32, y -32, 64, 64);
       }

       m_painter->addDirtyRect(QRect(x -32, y -32, 64, 64));
    }

    KisPaintOp * KisSmearyOpFactory::createOp(const KisPaintOpSettings */*settings*/, KisPainter * painter)
    {
       KisPaintOp * op = new KisSmearyOp(painter);
       Q_CHECK_PTR(op);
       return op;
    }

Ésto es todo: ¡las operaciones de dibujo son fáciles y divertidas!

## Extensiones de visualización

Las extensiones de visualización son las más extrañas del grupo: una extensión de visualización es un KPart ordinario que proporciona una pequeña interfaz de usuario y alguna funcionalidad. Por ejemplo, la pestaña histograma es una extensión de visualización, al igual que el diálogo rotar.

## Filtros de importación/exportación

&krita; trabaja la arquitectura habitual de filtro de archivos de &koffice;. Existe un tutorial, un poco antiguo, pero aún así práctico, en . Probablemente lo mejor es cooperar con el equipo de &krita; para desarrollar filtros de archivo y realizar el desarrollo en el árbol del filtro de &koffice;. Tenga en cuenta que puede probar sus filtros sin ejecutar &krita; utilizando la utilidad `koconverter`.

Los filtros tienen dos caras: importar y exportar. Existen usualmente dos extensiones diferentes que pueden compartir el mismo código.

Las entradas importantes de `Makefile.am` son:

    service_DATA = krita_XXX_import.desktop krita_XXX_export.desktop
    servicedir = $(kde_servicesdir)
    kdelnk_DATA = krita_XXX.desktop
    kdelnkdir = $(kde_appsdir)/Office
    libkritaXXXimport_la_SOURCES = XXXimport.cpp
    libkritaXXXexport_la_SOURCES = XXXexport.cpp
    METASOURCES = AUTO

Si está creando un filtro de importación o de exportación, su trabajo se reducirá siempre a implementar la siguiente función:

    virtual KoFilter::ConversionStatus convert(const QCString& from, const QCString& to);

Es la configuración en los archivos `.desktop` que determina la forma en que realiza la conversión un filtro:

Importar:

    X-KDE-Export=application/x-krita
    X-KDE-Import=image/x-xcf-gimp
    X-KDE-Weight=1
    X-KDE-Library=libkritaXXXimport
    ServiceTypes=KOfficeFilter

Exportar:

    X-KDE-Export=image/x-xcf-gimp
    X-KDE-Import=application/x-krita
    ServiceTypes=KOfficeFilter
    Type=Service
    X-KDE-Weight=1
    X-KDE-Library=libkritaXXXexport

Y sí, el tipo mime elegido para el ejemplo es una insinuación. Por favor, por favor, ¿por que no implementa un filtro xcf?

### Importar

El gran problema de los filtros de importación es, por supuesto, que su código debe leer datos del disco. El plato fuerte para llamar a este código es bastante simple:

Nota: realmente desearíamos encontrar una forma para permitir a &krita; mantener un archivo abierto y solo leer los datos de acuerdo con las necesidades, en lugar de copiar el contenido completo a la representación interna del dispositivo de dibujo. Pero esto puede significar que el motor de administración de datos debería conocer los archivos tiff, y así sucesivamente, y esto no está todavía implementado. Sería ideal si algunos filtros pudieran implementar una clase llamada provisionalmente `KisFileDataManager`, crear un objeto de esta instancia con el archivo actual y pasárselo a KisDoc. Pero &krita; maneja lo guardado por capa, no por documento, y podría ser complicado hacer una reescritura de esto.

    KoFilter::ConversionStatus XXXImport::convert(const QCString&, const QCString& to)
    {
       if (to != "application/x-krita")
           return KoFilter::BadMimeType;

       KisDoc * doc = dynamic_cast<KisDoc*>(m_chain -> outputDocument());
       KisView * view = static_cast<KisView*>(doc -> views().getFirst());

       QString filename = m_chain -> inputFile();

       if (!doc)
           return KoFilter::CreationError;

       doc -> prepareForImport();

       if (!filename.isEmpty()) {

           KURL url(filename);

           if (url.isEmpty())
               return KoFilter::FileNotFound;

           KisImageXXXConverter ib(doc, doc -> undoAdapter());

           if (view != 0)
               view -> canvasSubject() ->  progressDisplay() -> setSubject(&ib, false, true);

           switch (ib.buildImage(url))  {
               case KisImageBuilder_RESULT_UNSUPPORTED:
                   return KoFilter::NotImplemented;
                   break;
               case KisImageBuilder_RESULT_INVALID_ARG:
                   return KoFilter::BadMimeType;
                   break;
               case KisImageBuilder_RESULT_NO_URI:
               case KisImageBuilder_RESULT_NOT_LOCAL:
                   return KoFilter::FileNotFound;
                   break;
               case KisImageBuilder_RESULT_BAD_FETCH:
               case KisImageBuilder_RESULT_EMPTY:
                   return KoFilter::ParsingError;
                   break;
               case KisImageBuilder_RESULT_FAILURE:
                   return KoFilter::InternalError;
                   break;
               case KisImageBuilder_RESULT_OK:
                   doc -> setCurrentImage( ib.image());
                   return KoFilter::OK;
               default:
                   break;
           }

       }
       return KoFilter::StorageCreationError;
    }

Se supone que es un filtro de importación, por tanto sino se llama para convertir una imagen de &krita;, algo funcionará incorrectamente.

La cadena de filtrado habrá creado un documento de salida. Necesitamos convertirlo a `KisDocM`, porque los documentos de &krita; necesitan un tratamiento especial. No sería mala idea, no obstante, comprobar si el resultado de la conversión es distinto de 0, la importación fallará.

Si llamamos a este filtro desde la interfaz, intentará obtener la vista. Si existe un vista, el código de conversión puede intentar actualizar la barra de progreso.

El filtro tiene el nombre del archivo de entrada.

`KisDoc` necesita prepaparse para importar. Algunas configuraciones se inicializan y deshacer está desactivado. En caso contrario puede deshacer lo añadido a las capas realizadas por el filtro de importación y eso sería un comportamiento extraño.

Decidí implementar el código de importación actual en una clase separada que he instanciado aquí. También puede colocar todo su código correcto en este método, pero resultaría un poco confuso.

Los módulos de importación devuelven un código de estado que se puede utilizar para establecer el estado del filtro de importación. &koffice; lo tiene en cuenta a la hora de mostrar los mensajes de error.

Si creó `KisImage` correctamente podemos configurar la imagen actual del documento con la imagen que acaba de crear. Y con esto estará hecho: `return KoFilter::OK;`.