• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KImgIO

xcf.cpp

Go to the documentation of this file.
00001 /*
00002  * qxcfi.cpp: A Qt 3 plug-in for reading GIMP XCF image files
00003  * Copyright (C) 2001 lignum Computing, Inc. <allen@lignumcomputing.com>
00004  * Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org>
00005  *
00006  * This plug-in is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00019  *
00020  */
00021 
00022 #include "xcf.h"
00023 
00024 #include <stdlib.h>
00025 #include <QtGui/QImage>
00026 #include <QtCore/QIODevice>
00027 #include <QtCore/QStack>
00028 #include <QtCore/QVector>
00029 
00030 #include <kdebug.h>
00031 
00032 
00033 int XCFImageFormat::random_table[RANDOM_TABLE_SIZE];
00034 
00035 //int XCFImageFormat::add_lut[256][256];
00036 
00037 
00038 const XCFImageFormat::LayerModes XCFImageFormat::layer_modes[] = {
00039     {true},     // NORMAL_MODE
00040     {true},     // DISSOLVE_MODE
00041     {true},     // BEHIND_MODE
00042     {false},    // MULTIPLY_MODE
00043     {false},    // SCREEN_MODE
00044     {false},    // OVERLAY_MODE
00045     {false},    // DIFFERENCE_MODE
00046     {false},    // ADDITION_MODE
00047     {false},    // SUBTRACT_MODE
00048     {false},    // DARKEN_ONLY_MODE
00049     {false},    // LIGHTEN_ONLY_MODE
00050     {false},    // HUE_MODE
00051     {false},    // SATURATION_MODE
00052     {false},    // COLOR_MODE
00053     {false},    // VALUE_MODE
00054     {false},    // DIVIDE_MODE
00055     {false},    // DODGE_MODE
00056     {false},    // BURN_MODE
00057     {false},    // HARDLIGHT_MODE
00058     {false},    // SOFTLIGHT_MODE
00059     {false},    // GRAIN_EXTRACT_MODE
00060     {false},    // GRAIN_MERGE_MODE
00061 };
00062 
00063 
00065 inline QRgb qRgba ( const QRgb& rgb, int a )
00066 {
00067     return ((a & 0xff) << 24 | (rgb & RGB_MASK));
00068 }
00069 
00070 
00075 XCFImageFormat::XCFImageFormat()
00076 {
00077     // From GIMP "paint_funcs.c" v1.2
00078     srand(RANDOM_SEED);
00079 
00080     for (int i = 0; i < RANDOM_TABLE_SIZE; i++)
00081         random_table[i] = rand();
00082 
00083     for (int i = 0; i < RANDOM_TABLE_SIZE; i++) {
00084         int tmp;
00085         int swap = i + rand() % (RANDOM_TABLE_SIZE - i);
00086         tmp = random_table[i];
00087         random_table[i] = random_table[swap];
00088         random_table[swap] = tmp;
00089     }
00090 
00091 //  for (int j = 0; j < 256; j++) {
00092 //      for (int k = 0; k < 256; k++) {
00093 //          int tmp_sum = j + k;
00094 //          if (tmp_sum > 255)
00095 //              tmp_sum = 255;
00096 //          add_lut[j][k] = tmp_sum;
00097 //      }
00098 //  }
00099 }
00100 
00101 inline
00102 int XCFImageFormat::add_lut( int a, int b ) {
00103     return qMin( a + b, 255 );
00104 }
00105 
00106 bool XCFImageFormat::readXCF(QIODevice *device, QImage *outImage)
00107 {
00108     XCFImage xcf_image;
00109     QDataStream xcf_io(device);
00110 
00111     char tag[14];;
00112 
00113     if (xcf_io.readRawData(tag, sizeof(tag)) != sizeof(tag)) {
00114             kDebug(399) << "XCF: read failure on header tag";
00115             return false;
00116     }
00117 
00118     xcf_io >> xcf_image.width >> xcf_image.height >> xcf_image.type;
00119 
00120 kDebug() << tag << " " << xcf_image.width << " " << xcf_image.height << " " <<  xcf_image.type;
00121     if (!loadImageProperties(xcf_io, xcf_image))
00122         return false;
00123 
00124     // The layers appear to be stored in top-to-bottom order. This is
00125     // the reverse of how a merged image must be computed. So, the layer
00126     // offsets are pushed onto a LIFO stack (thus, we don't have to load
00127     // all the data of all layers before beginning to construct the
00128     // merged image).
00129 
00130     QStack<qint32> layer_offsets;
00131 
00132     while (true) {
00133         qint32 layer_offset;
00134 
00135         xcf_io >> layer_offset;
00136 
00137         if (layer_offset == 0)
00138             break;
00139 
00140         layer_offsets.push(layer_offset);
00141     }
00142 
00143     xcf_image.num_layers = layer_offsets.size();
00144 
00145     if (layer_offsets.size() == 0) {
00146         kDebug(399) << "XCF: no layers!";
00147         return false;
00148     }
00149 
00150     // Load each layer and add it to the image
00151     while (!layer_offsets.isEmpty()) {
00152         qint32 layer_offset = layer_offsets.pop();
00153 
00154         xcf_io.device()->seek(layer_offset);
00155 
00156         if (!loadLayer(xcf_io, xcf_image))
00157             return false;
00158     }
00159 
00160     if (!xcf_image.initialized) {
00161         kDebug(399) << "XCF: no visible layers!";
00162         return false;
00163     }
00164 
00165         *outImage = xcf_image.image;
00166         return true;
00167 }
00168 
00169 
00177 bool XCFImageFormat::loadImageProperties(QDataStream& xcf_io, XCFImage& xcf_image)
00178 {
00179     while (true) {
00180         PropType type;
00181         QByteArray bytes;
00182 
00183         if (!loadProperty(xcf_io, type, bytes)) {
00184             kDebug(399) << "XCF: error loading global image properties";
00185             return false;
00186         }
00187 
00188         QDataStream property(bytes);
00189 
00190         switch (type) {
00191             case PROP_END:
00192                 return true;
00193 
00194             case PROP_COMPRESSION:
00195                 property >> xcf_image.compression;
00196                 break;
00197 
00198             case PROP_RESOLUTION:
00199                 property >> xcf_image.x_resolution >> xcf_image.y_resolution;
00200                 break;
00201 
00202             case PROP_TATTOO:
00203                 property >> xcf_image.tattoo;
00204                 break;
00205 
00206             case PROP_PARASITES:
00207                 while (!property.atEnd()) {
00208                     char* tag;
00209                     quint32 size;
00210 
00211                     property.readBytes(tag, size);
00212 
00213                     quint32 flags;
00214                     char* data=0;
00215                     property >> flags >> data;
00216 
00217                     if (tag && strncmp(tag, "gimp-comment", strlen("gimp-comment")) == 0)
00218                         xcf_image.image.setText("Comment", 0, data);
00219 
00220                     delete[] tag;
00221                     delete[] data;
00222                 }
00223                 break;
00224 
00225                 case PROP_UNIT:
00226                     property >> xcf_image.unit;
00227                     break;
00228 
00229                 case PROP_PATHS:    // This property is ignored.
00230                     break;
00231 
00232                 case PROP_USER_UNIT:    // This property is ignored.
00233                     break;
00234 
00235                 case PROP_COLORMAP:
00236                     property >> xcf_image.num_colors;
00237                                         if(xcf_image.num_colors < 0 || xcf_image.num_colors > 65535)
00238                                             return false;
00239 
00240                     xcf_image.palette.reserve(xcf_image.num_colors);
00241 
00242                     for (int i = 0; i < xcf_image.num_colors; i++) {
00243                         uchar r, g, b;
00244                         property >> r >> g >> b;
00245                         xcf_image.palette.push_back( qRgb(r,g,b) );
00246                     }
00247                     break;
00248 
00249                 default:
00250                     kDebug(399) << "XCF: unimplemented image property" << type
00251                             << ", size " << bytes.size() << endl;
00252         }
00253     }
00254 }
00255 
00256 
00264 bool XCFImageFormat::loadProperty(QDataStream& xcf_io, PropType& type, QByteArray& bytes)
00265 {
00266     quint32 foo;
00267     xcf_io >> foo;
00268     type=PropType(foo); // TODO urks
00269 
00270     char* data = 0;
00271     quint32 size;
00272 
00273     // The colormap property size is not the correct number of bytes:
00274     // The GIMP source xcf.c has size = 4 + ncolors, but it should be
00275     // 4 + 3 * ncolors
00276 
00277     if (type == PROP_COLORMAP) {
00278         xcf_io >> size;
00279 
00280                 if(size > 65535 || size < 4)
00281                     return false;
00282 
00283         size = 3 * (size - 4) + 4;
00284         data = new char[size];
00285 
00286         xcf_io.readRawData(data, size);
00287     } else if (type == PROP_USER_UNIT) {
00288         // The USER UNIT property size is not correct. I'm not sure why, though.
00289         float factor;
00290         qint32 digits;
00291 
00292         xcf_io >> size >> factor >> digits;
00293 
00294         for (int i = 0; i < 5; i++) {
00295             char* unit_strings;
00296 
00297             xcf_io >> unit_strings;
00298 
00299             delete[] unit_strings;
00300 
00301             if (xcf_io.device()->atEnd()) {
00302                 kDebug(399) << "XCF: read failure on property " << type;
00303                 return false;
00304             }
00305         }
00306 
00307         size = 0;
00308     } else {
00309                 xcf_io >> size;
00310                 if(size >256000)
00311                     return false;
00312                 data = new char[size];
00313         xcf_io.readRawData(data, size);
00314         }
00315 
00316     if (size != 0 && data)
00317             bytes = QByteArray(data,size);
00318     
00319         delete [] data;
00320 
00321     return true;
00322 }
00323 
00324 
00333 bool XCFImageFormat::loadLayer(QDataStream& xcf_io, XCFImage& xcf_image)
00334 {
00335     Layer& layer(xcf_image.layer);
00336     delete[] layer.name;
00337 
00338     xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
00339 
00340     if (!loadLayerProperties(xcf_io, layer))
00341         return false;
00342 #if 0
00343   cout << "layer: \"" << layer.name << "\", size: " << layer.width << " x "
00344        << layer.height << ", type: " << layer.type << ", mode: " << layer.mode
00345        << ", opacity: " << layer.opacity << ", visible: " << layer.visible
00346        << ", offset: " << layer.x_offset << ", " << layer.y_offset << endl;
00347 #endif
00348   // Skip reading the rest of it if it is not visible. Typically, when
00349   // you export an image from the The GIMP it flattens (or merges) only
00350   // the visible layers into the output image.
00351 
00352     if (layer.visible == 0)
00353         return true;
00354 
00355     // If there are any more layers, merge them into the final QImage.
00356 
00357     xcf_io >> layer.hierarchy_offset >> layer.mask_offset;
00358 
00359     // Allocate the individual tile QImages based on the size and type
00360     // of this layer.
00361 
00362     if( !composeTiles(xcf_image))
00363         return false;
00364     xcf_io.device()->seek(layer.hierarchy_offset);
00365 
00366     // As tiles are loaded, they are copied into the layers tiles by
00367     // this routine. (loadMask(), below, uses a slightly different
00368     // version of assignBytes().)
00369 
00370     layer.assignBytes = assignImageBytes;
00371 
00372     if (!loadHierarchy(xcf_io, layer))
00373         return false;
00374 
00375     if (layer.mask_offset != 0) {
00376         xcf_io.device()->seek(layer.mask_offset);
00377 
00378         if (!loadMask(xcf_io, layer))
00379             return false;
00380     }
00381 
00382     // Now we should have enough information to initialize the final
00383     // QImage. The first visible layer determines the attributes
00384     // of the QImage.
00385 
00386     if (!xcf_image.initialized) {
00387         if( !initializeImage(xcf_image))
00388             return false;
00389         copyLayerToImage(xcf_image);
00390         xcf_image.initialized = true;
00391     } else
00392         mergeLayerIntoImage(xcf_image);
00393 
00394     return true;
00395 }
00396 
00397 
00405 bool XCFImageFormat::loadLayerProperties(QDataStream& xcf_io, Layer& layer)
00406 {
00407     while (true) {
00408         PropType type;
00409         QByteArray bytes;
00410 
00411         if (!loadProperty(xcf_io, type, bytes)) {
00412             kDebug(399) << "XCF: error loading layer properties";
00413             return false;
00414         }
00415 
00416         QDataStream property(bytes);
00417 
00418         switch (type) {
00419             case PROP_END:
00420                 return true;
00421 
00422             case PROP_ACTIVE_LAYER:
00423                 layer.active = true;
00424                 break;
00425 
00426             case PROP_OPACITY:
00427                 property >> layer.opacity;
00428                 break;
00429 
00430             case PROP_VISIBLE:
00431                 property >> layer.visible;
00432                 break;
00433 
00434             case PROP_LINKED:
00435                 property >> layer.linked;
00436                 break;
00437 
00438             case PROP_PRESERVE_TRANSPARENCY:
00439                 property >> layer.preserve_transparency;
00440                 break;
00441 
00442             case PROP_APPLY_MASK:
00443                 property >> layer.apply_mask;
00444                 break;
00445 
00446             case PROP_EDIT_MASK:
00447                 property >> layer.edit_mask;
00448                 break;
00449 
00450             case PROP_SHOW_MASK:
00451                 property >> layer.show_mask;
00452                 break;
00453 
00454             case PROP_OFFSETS:
00455                 property >> layer.x_offset >> layer.y_offset;
00456                 break;
00457 
00458             case PROP_MODE:
00459                 property >> layer.mode;
00460                 break;
00461 
00462             case PROP_TATTOO:
00463                 property >> layer.tattoo;
00464                 break;
00465 
00466             default:
00467                 kDebug(399) << "XCF: unimplemented layer property " << type
00468                         << ", size " << bytes.size() << endl;
00469         }
00470     }
00471 }
00472 
00473 
00479 bool XCFImageFormat::composeTiles(XCFImage& xcf_image)
00480 {
00481     Layer& layer(xcf_image.layer);
00482 
00483     layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
00484     layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
00485 
00486     layer.image_tiles.resize(layer.nrows);
00487 
00488     if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE)
00489         layer.alpha_tiles.resize(layer.nrows);
00490 
00491     if (layer.mask_offset != 0)
00492         layer.mask_tiles.resize(layer.nrows);
00493 
00494     for (uint j = 0; j < layer.nrows; j++) {
00495         layer.image_tiles[j].resize(layer.ncols);
00496 
00497         if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE)
00498             layer.alpha_tiles[j].resize(layer.ncols);
00499 
00500         if (layer.mask_offset != 0)
00501             layer.mask_tiles[j].resize(layer.ncols);
00502     }
00503 
00504     for (uint j = 0; j < layer.nrows; j++) {
00505         for (uint i = 0; i < layer.ncols; i++) {
00506 
00507             uint tile_width = (i + 1) * TILE_WIDTH <= layer.width
00508                     ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
00509 
00510             uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height
00511                     ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
00512 
00513             // Try to create the most appropriate QImage (each GIMP layer
00514             // type is treated slightly differently)
00515 
00516             switch (layer.type) {
00517                 case RGB_GIMAGE:
00518                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_RGB32);
00519                     layer.image_tiles[j][i].setNumColors(0);
00520                     if( layer.image_tiles[j][i].isNull())
00521                         return false;
00522                     break;
00523 
00524                 case RGBA_GIMAGE:
00525                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_ARGB32);
00526                     layer.image_tiles[j][i].setNumColors(0);
00527                     if( layer.image_tiles[j][i].isNull())
00528                         return false;
00529                     break;
00530 
00531                 case GRAY_GIMAGE:
00532                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00533                     layer.image_tiles[j][i].setNumColors(256);
00534                     if( layer.image_tiles[j][i].isNull())
00535                         return false;
00536                     setGrayPalette(layer.image_tiles[j][i]);
00537                     break;
00538 
00539                 case GRAYA_GIMAGE:
00540                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00541                     layer.image_tiles[j][i].setNumColors(256);
00542                     if( layer.image_tiles[j][i].isNull())
00543                         return false;
00544                     setGrayPalette(layer.image_tiles[j][i]);
00545 
00546                     layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00547                     layer.alpha_tiles[j][i].setNumColors(256);
00548                     if( layer.alpha_tiles[j][i].isNull())
00549                         return false;
00550                     setGrayPalette(layer.alpha_tiles[j][i]);
00551                     break;
00552 
00553                 case INDEXED_GIMAGE:
00554                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00555                     layer.image_tiles[j][i].setNumColors(xcf_image.num_colors);
00556                     if( layer.image_tiles[j][i].isNull())
00557                         return false;
00558                     setPalette(xcf_image, layer.image_tiles[j][i]);
00559                     break;
00560 
00561                 case INDEXEDA_GIMAGE:
00562                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00563                     layer.image_tiles[j][i].setNumColors(xcf_image.num_colors);
00564                     if( layer.image_tiles[j][i].isNull())
00565                         return false;
00566                     setPalette(xcf_image, layer.image_tiles[j][i]);
00567 
00568                     layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00569                     layer.alpha_tiles[j][i].setNumColors(256);
00570                     if( layer.alpha_tiles[j][i].isNull())
00571                         return false;
00572                     setGrayPalette(layer.alpha_tiles[j][i]);
00573             }
00574 
00575             if (layer.mask_offset != 0) {
00576                 layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00577                 layer.mask_tiles[j][i].setNumColors(256);
00578                 if( layer.mask_tiles[j][i].isNull())
00579                     return false;
00580                 setGrayPalette(layer.mask_tiles[j][i]);
00581             }
00582         }
00583     }
00584     return true;
00585 }
00586 
00587 
00594 void XCFImageFormat::setGrayPalette(QImage& image)
00595 {
00596     for (int i = 0; i < 256; i++)
00597         image.setColor(i, qRgb(i, i, i));
00598 }
00599 
00600 
00606 void XCFImageFormat::setPalette(XCFImage& xcf_image, QImage& image)
00607 {
00608     for (int i = 0; i < xcf_image.num_colors; i++)
00609         image.setColor(i, xcf_image.palette[i]);
00610 }
00611 
00612 
00620 void XCFImageFormat::assignImageBytes(Layer& layer, uint i, uint j)
00621 {
00622     uchar* tile = layer.tile;
00623 
00624     switch (layer.type) {
00625         case RGB_GIMAGE:
00626             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
00627                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
00628                     layer.image_tiles[j][i].setPixel(k, l,
00629                             qRgb(tile[0], tile[1], tile[2]));
00630                     tile += sizeof(QRgb);
00631                 }
00632             }
00633             break;
00634 
00635         case RGBA_GIMAGE:
00636             for ( int l = 0; l < layer.image_tiles[j][i].height(); l++ ) {
00637                 for ( int k = 0; k < layer.image_tiles[j][i].width(); k++ ) {
00638                     layer.image_tiles[j][i].setPixel(k, l,
00639                             qRgba(tile[0], tile[1], tile[2], tile[3]));
00640                     tile += sizeof(QRgb);
00641                 }
00642             }
00643             break;
00644 
00645         case GRAY_GIMAGE:
00646         case INDEXED_GIMAGE:
00647             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
00648                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
00649                     layer.image_tiles[j][i].setPixel(k, l, tile[0]);
00650                     tile += sizeof(QRgb);
00651                 }
00652             }
00653             break;
00654 
00655         case GRAYA_GIMAGE:
00656         case INDEXEDA_GIMAGE:
00657             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
00658                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
00659 
00660                 // The "if" here should not be necessary, but apparently there
00661                 // are some cases where the image can contain larger indices
00662                 // than there are colors in the palette. (A bug in The GIMP?)
00663 
00664                     if (tile[0] < layer.image_tiles[j][i].numColors())
00665                         layer.image_tiles[j][i].setPixel(k, l, tile[0]);
00666 
00667                     layer.alpha_tiles[j][i].setPixel(k, l, tile[1]);
00668                     tile += sizeof(QRgb);
00669                 }
00670             }
00671             break;
00672     }
00673 }
00674 
00675 
00684 bool XCFImageFormat::loadHierarchy(QDataStream& xcf_io, Layer& layer)
00685 {
00686     qint32 width;
00687     qint32 height;
00688     qint32 bpp;
00689     quint32 offset;
00690 
00691     xcf_io >> width >> height >> bpp >> offset;
00692 
00693     // GIMP stores images in a "mipmap"-like format (multiple levels of
00694     // increasingly lower resolution). Only the top level is used here,
00695     // however.
00696 
00697     quint32 junk;
00698     do {
00699         xcf_io >> junk;
00700 
00701         if (xcf_io.device()->atEnd()) {
00702             kDebug(399) << "XCF: read failure on layer " << layer.name << " level offsets";
00703             return false;
00704         }
00705     } while (junk != 0);
00706 
00707     qint64 saved_pos = xcf_io.device()->pos();
00708 
00709     xcf_io.device()->seek(offset);
00710     if (!loadLevel(xcf_io, layer, bpp))
00711         return false;
00712 
00713     xcf_io.device()->seek(saved_pos);
00714     return true;
00715 }
00716 
00717 
00726 bool XCFImageFormat::loadLevel(QDataStream& xcf_io, Layer& layer, qint32 bpp)
00727 {
00728     qint32 width;
00729     qint32 height;
00730     quint32 offset;
00731 
00732     xcf_io >> width >> height >> offset;
00733 
00734     if (offset == 0)
00735         return true;
00736 
00737     for (uint j = 0; j < layer.nrows; j++) {
00738         for (uint i = 0; i < layer.ncols; i++) {
00739 
00740             if (offset == 0) {
00741                 kDebug(399) << "XCF: incorrect number of tiles in layer " << layer.name;
00742                 return false;
00743             }
00744 
00745             qint64 saved_pos = xcf_io.device()->pos();
00746             quint32 offset2;
00747             xcf_io >> offset2;
00748 
00749             // Evidently, RLE can occasionally expand a tile instead of compressing it!
00750 
00751             if (offset2 == 0)
00752                 offset2 = offset + (uint)(TILE_WIDTH * TILE_HEIGHT * 4 * 1.5);
00753 
00754             xcf_io.device()->seek(offset);
00755             int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
00756 
00757             if (!loadTileRLE(xcf_io, layer.tile, size, offset2 - offset, bpp))
00758                 return false;
00759 
00760             // The bytes in the layer tile are juggled differently depending on
00761             // the target QImage. The caller has set layer.assignBytes to the
00762             // appropriate routine.
00763 
00764             layer.assignBytes(layer, i, j);
00765 
00766             xcf_io.device()->seek(saved_pos);
00767             xcf_io >> offset;
00768         }
00769     }
00770 
00771     return true;
00772 }
00773 
00774 
00781 bool XCFImageFormat::loadMask(QDataStream& xcf_io, Layer& layer)
00782 {
00783     qint32 width;
00784     qint32 height;
00785     char* name;
00786 
00787     xcf_io >> width >> height >> name;
00788 
00789     delete name;
00790 
00791     if (!loadChannelProperties(xcf_io, layer))
00792         return false;
00793 
00794     quint32 hierarchy_offset;
00795     xcf_io >> hierarchy_offset;
00796 
00797     xcf_io.device()->seek(hierarchy_offset);
00798     layer.assignBytes = assignMaskBytes;
00799 
00800     if (!loadHierarchy(xcf_io, layer))
00801         return false;
00802 
00803     return true;
00804 }
00805 
00806 
00830 bool XCFImageFormat::loadTileRLE(QDataStream& xcf_io, uchar* tile, int image_size,
00831         int data_length, qint32 bpp)
00832 {
00833     uchar* data;
00834 
00835     uchar* xcfdata;
00836     uchar* xcfodata;
00837     uchar* xcfdatalimit;
00838 
00839     xcfdata = xcfodata = new uchar[data_length];
00840 
00841     xcf_io.readRawData((char*)xcfdata, data_length);
00842 
00843     if (!xcf_io.device()->isOpen()) {
00844         delete[] xcfodata;
00845         kDebug(399) << "XCF: read failure on tile";
00846         return false;
00847     }
00848 
00849     xcfdatalimit = &xcfodata[data_length - 1];
00850 
00851     for (int i = 0; i < bpp; ++i) {
00852 
00853         data = tile + i;
00854 
00855         int count = 0;
00856         int size = image_size;
00857 
00858         while (size > 0) {
00859             if (xcfdata > xcfdatalimit)
00860                 goto bogus_rle;
00861 
00862             uchar val = *xcfdata++;
00863             uint length = val;
00864 
00865             if (length >= 128) {
00866                 length = 255 - (length - 1);
00867                 if (length == 128) {
00868                     if (xcfdata >= xcfdatalimit)
00869                         goto bogus_rle;
00870 
00871                     length = (*xcfdata << 8) + xcfdata[1];
00872 
00873                     xcfdata += 2;
00874                 }
00875 
00876                 count += length;
00877                 size -= length;
00878 
00879                 if (size < 0)
00880                     goto bogus_rle;
00881 
00882                 if (&xcfdata[length - 1] > xcfdatalimit)
00883                     goto bogus_rle;
00884 
00885                 while (length-- > 0) {
00886                     *data = *xcfdata++;
00887                     data += sizeof(QRgb);
00888                 }
00889             } else {
00890                 length += 1;
00891                 if (length == 128) {
00892                     if (xcfdata >= xcfdatalimit)
00893                         goto bogus_rle;
00894 
00895                     length = (*xcfdata << 8) + xcfdata[1];
00896                     xcfdata += 2;
00897                 }
00898 
00899                 count += length;
00900                 size -= length;
00901 
00902                 if (size < 0)
00903                     goto bogus_rle;
00904 
00905                 if (xcfdata > xcfdatalimit)
00906                     goto bogus_rle;
00907 
00908                 val = *xcfdata++;
00909 
00910                 while (length-- > 0) {
00911                     *data = val;
00912                     data += sizeof(QRgb);
00913                 }
00914             }
00915         }
00916     }
00917 
00918     delete[] xcfodata;
00919     return true;
00920 
00921 bogus_rle:
00922 
00923     kDebug(399) << "The run length encoding could not be decoded properly";
00924     delete[] xcfodata;
00925     return false;
00926 }
00927 
00928 
00936 bool XCFImageFormat::loadChannelProperties(QDataStream& xcf_io, Layer& layer)
00937 {
00938     while (true) {
00939         PropType type;
00940         QByteArray bytes;
00941 
00942         if (!loadProperty(xcf_io, type, bytes)) {
00943             kDebug(399) << "XCF: error loading channel properties";
00944             return false;
00945         }
00946 
00947         QDataStream property(bytes);
00948 
00949         switch (type) {
00950             case PROP_END:
00951                 return true;
00952 
00953             case PROP_OPACITY:
00954                 property >> layer.mask_channel.opacity;
00955                 break;
00956 
00957             case PROP_VISIBLE:
00958                 property >> layer.mask_channel.visible;
00959                 break;
00960 
00961             case PROP_SHOW_MASKED:
00962                 property >> layer.mask_channel.show_masked;
00963                 break;
00964 
00965             case PROP_COLOR:
00966                 property >> layer.mask_channel.red >> layer.mask_channel.green
00967                         >> layer.mask_channel.blue;
00968                 break;
00969 
00970             case PROP_TATTOO:
00971                 property >> layer.mask_channel.tattoo;
00972                 break;
00973 
00974             default:
00975                 kDebug(399) << "XCF: unimplemented channel property " << type
00976                         << ", size " << bytes.size() << endl;
00977         }
00978     }
00979 }
00980 
00981 
00988 void XCFImageFormat::assignMaskBytes(Layer& layer, uint i, uint j)
00989 {
00990     uchar* tile = layer.tile;
00991 
00992     for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
00993         for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
00994             layer.mask_tiles[j][i].setPixel(k, l, tile[0]);
00995             tile += sizeof(QRgb);
00996         }
00997     }
00998 }
00999 
01000 
01029 bool XCFImageFormat::initializeImage(XCFImage& xcf_image)
01030 {
01031     // (Aliases to make the code look a little better.)
01032     Layer& layer(xcf_image.layer);
01033     QImage& image(xcf_image.image);
01034 
01035     switch (layer.type) {
01036         case RGB_GIMAGE:
01037             if (layer.opacity == OPAQUE_OPACITY) {
01038                 image = QImage( xcf_image.width, xcf_image.height, QImage::Format_RGB32);
01039                 if( image.isNull())
01040                     return false;
01041                 image.fill(qRgb(255, 255, 255));
01042                 break;
01043             } // else, fall through to 32-bit representation
01044 
01045         case RGBA_GIMAGE:
01046             image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01047             if( image.isNull())
01048                 return false;
01049             image.fill(qRgba(255, 255, 255, 0));
01050             break;
01051 
01052         case GRAY_GIMAGE:
01053             if (layer.opacity == OPAQUE_OPACITY) {
01054                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01055                 image.setNumColors(256);
01056                 if( image.isNull())
01057                     return false;
01058                 setGrayPalette(image);
01059                 image.fill(255);
01060                 break;
01061             } // else, fall through to 32-bit representation
01062 
01063         case GRAYA_GIMAGE:
01064             image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01065             if( image.isNull())
01066                 return false;
01067             image.fill(qRgba(255, 255, 255, 0));
01068             break;
01069 
01070         case INDEXED_GIMAGE:
01071             // As noted in the table above, there are quite a few combinations
01072             // which are possible with indexed images, depending on the
01073             // presence of transparency (note: not translucency, which is not
01074             // supported by The GIMP for indexed images) and the number of
01075             // individual colors.
01076 
01077             // Note: Qt treats a bitmap with a Black and White color palette
01078             // as a mask, so only the "on" bits are drawn, regardless of the
01079             // order color table entries. Otherwise (i.e., at least one of the
01080             // color table entries is not black or white), it obeys the one-
01081             // or two-color palette. Have to ask about this...
01082 
01083             if (xcf_image.num_colors <= 2) {
01084                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
01085                 image.setNumColors(xcf_image.num_colors);
01086                 if( image.isNull())
01087                     return false;
01088                 image.fill(0);
01089                 setPalette(xcf_image, image);
01090             } else if (xcf_image.num_colors <= 256) {
01091                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01092                 image.setNumColors(xcf_image.num_colors);
01093                 if( image.isNull())
01094                     return false;
01095                 image.fill(0);
01096                 setPalette(xcf_image, image);
01097             }
01098             break;
01099 
01100         case INDEXEDA_GIMAGE:
01101             if (xcf_image.num_colors == 1) {
01102                 // Plenty(!) of room to add a transparent color
01103                 xcf_image.num_colors++;
01104                 xcf_image.palette.resize(xcf_image.num_colors);
01105                 xcf_image.palette[1] = xcf_image.palette[0];
01106                 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
01107 
01108                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
01109                 image.setNumColors(xcf_image.num_colors);
01110                 if( image.isNull())
01111                     return false;
01112                 image.fill(0);
01113                 setPalette(xcf_image, image);
01114             } else if (xcf_image.num_colors < 256) {
01115                 // Plenty of room to add a transparent color
01116                 xcf_image.num_colors++;
01117                 xcf_image.palette.resize(xcf_image.num_colors);
01118                 for (int c = xcf_image.num_colors - 1; c >= 1; c--)
01119                     xcf_image.palette[c] = xcf_image.palette[c - 1];
01120 
01121                 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
01122                 image = QImage( xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01123                 image.setNumColors(xcf_image.num_colors);
01124                 if( image.isNull())
01125                     return false;
01126                 image.fill(0);
01127                 setPalette(xcf_image, image);
01128             } else {
01129                 // No room for a transparent color, so this has to be promoted to
01130                 // true color. (There is no equivalent PNG representation output
01131                 // from The GIMP as of v1.2.)
01132                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01133                 if( image.isNull())
01134                     return false;
01135                 image.fill(qRgba(255, 255, 255, 0));
01136             }
01137             break;
01138     }
01139 
01140     image.setDotsPerMeterX((int)(xcf_image.x_resolution * INCHESPERMETER));
01141     image.setDotsPerMeterY((int)(xcf_image.y_resolution * INCHESPERMETER));
01142     return true;
01143 }
01144 
01145 
01151 void XCFImageFormat::copyLayerToImage(XCFImage& xcf_image)
01152 {
01153     Layer& layer(xcf_image.layer);
01154     QImage& image(xcf_image.image);
01155     PixelCopyOperation copy = 0;
01156 
01157     switch (layer.type) {
01158         case RGB_GIMAGE:
01159         case RGBA_GIMAGE:
01160             copy = copyRGBToRGB;
01161             break;
01162         case GRAY_GIMAGE:
01163             if (layer.opacity == OPAQUE_OPACITY)
01164                 copy = copyGrayToGray;
01165             else
01166                 copy = copyGrayToRGB;
01167             break;
01168         case GRAYA_GIMAGE:
01169             copy = copyGrayAToRGB;
01170             break;
01171         case INDEXED_GIMAGE:
01172             copy = copyIndexedToIndexed;
01173             break;
01174         case INDEXEDA_GIMAGE:
01175             if (xcf_image.image.depth() <= 8)
01176                 copy = copyIndexedAToIndexed;
01177             else
01178                 copy = copyIndexedAToRGB;
01179     }
01180 
01181     // For each tile...
01182 
01183     for (uint j = 0; j < layer.nrows; j++) {
01184         uint y = j * TILE_HEIGHT;
01185 
01186         for (uint i = 0; i < layer.ncols; i++) {
01187             uint x = i * TILE_WIDTH;
01188 
01189             // This seems the best place to apply the dissolve because it
01190             // depends on the global position of each tile's
01191             // pixels. Apparently it's the only mode which can apply to a
01192             // single layer.
01193 
01194             if (layer.mode == DISSOLVE_MODE) {
01195                 if (layer.type == RGBA_GIMAGE)
01196                     dissolveRGBPixels(layer.image_tiles[j][i], x, y);
01197 
01198                 else if (layer.type == GRAYA_GIMAGE)
01199                     dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
01200             }
01201 
01202             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01203                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01204 
01205                     int m = x + k + layer.x_offset;
01206                     int n = y + l + layer.y_offset;
01207 
01208                     if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
01209                         continue;
01210 
01211                     (*copy)(layer, i, j, k, l, image, m, n);
01212                 }
01213             }
01214         }
01215     }
01216 }
01217 
01218 
01232 void XCFImageFormat::copyRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
01233         QImage& image, int m, int n)
01234 {
01235     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01236     uchar src_a = layer.opacity;
01237 
01238     if (layer.type == RGBA_GIMAGE)
01239         src_a = INT_MULT(src_a, qAlpha(src));
01240 
01241     // Apply the mask (if any)
01242 
01243     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01244             layer.mask_tiles[j].size() > (int)i)
01245         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01246 
01247     image.setPixel(m, n, qRgba(src, src_a));
01248 }
01249 
01250 
01262 void XCFImageFormat::copyGrayToGray(Layer& layer, uint i, uint j, int k, int l,
01263         QImage& image, int m, int n)
01264 {
01265     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01266     image.setPixel(m, n, src);
01267 }
01268 
01269 
01283 void XCFImageFormat::copyGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
01284         QImage& image, int m, int n)
01285 {
01286     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01287     uchar src_a = layer.opacity;
01288     image.setPixel(m, n, qRgba(src, src_a));
01289 }
01290 
01291 
01305 void XCFImageFormat::copyGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
01306                       QImage& image, int m, int n)
01307 {
01308     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01309     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01310     src_a = INT_MULT(src_a, layer.opacity);
01311 
01312     // Apply the mask (if any)
01313 
01314     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01315             layer.mask_tiles[j].size() > (int)i)
01316         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01317 
01318     image.setPixel(m, n, qRgba(src, src_a));
01319 }
01320 
01321 
01333 void XCFImageFormat::copyIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
01334         QImage& image, int m, int n)
01335 {
01336     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01337     image.setPixel(m, n, src);
01338 }
01339 
01340 
01352 void XCFImageFormat::copyIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
01353         QImage& image, int m, int n)
01354 {
01355     uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
01356     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01357     src_a = INT_MULT(src_a, layer.opacity);
01358 
01359     if (layer.apply_mask == 1 &&
01360             layer.mask_tiles.size() > (int)j &&
01361             layer.mask_tiles[j].size() > (int)i)
01362         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01363 
01364     if (src_a > 127)
01365         src++;
01366     else
01367         src = 0;
01368 
01369 image.setPixel(m, n, src);
01370 }
01371 
01372 
01386 void XCFImageFormat::copyIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
01387         QImage& image, int m, int n)
01388 {
01389     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01390     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01391     src_a = INT_MULT(src_a, layer.opacity);
01392 
01393     // Apply the mask (if any)
01394     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01395             layer.mask_tiles[j].size() > (int)i)
01396         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01397 
01398     // This is what appears in the GIMP window
01399     if (src_a <= 127)
01400         src_a = 0;
01401     else
01402         src_a = OPAQUE_OPACITY;
01403 
01404     image.setPixel(m, n, qRgba(src, src_a));
01405 }
01406 
01407 
01412 void XCFImageFormat::mergeLayerIntoImage(XCFImage& xcf_image)
01413 {
01414     Layer& layer(xcf_image.layer);
01415     QImage& image(xcf_image.image);
01416 
01417     PixelMergeOperation merge = 0;
01418 
01419     if (!layer.opacity) return; // don't bother doing anything
01420 
01421     switch (layer.type) {
01422         case RGB_GIMAGE:
01423         case RGBA_GIMAGE:
01424             merge = mergeRGBToRGB;
01425             break;
01426         case GRAY_GIMAGE:
01427             if (layer.opacity == OPAQUE_OPACITY)
01428                 merge = mergeGrayToGray;
01429             else
01430                 merge = mergeGrayToRGB;
01431             break;
01432         case GRAYA_GIMAGE:
01433             if (xcf_image.image.depth() <= 8)
01434                 merge = mergeGrayAToGray;
01435             else
01436                 merge = mergeGrayAToRGB;
01437             break;
01438         case INDEXED_GIMAGE:
01439             merge = mergeIndexedToIndexed;
01440             break;
01441         case INDEXEDA_GIMAGE:
01442             if (xcf_image.image.depth() <= 8)
01443                 merge = mergeIndexedAToIndexed;
01444             else
01445                 merge = mergeIndexedAToRGB;
01446     }
01447 
01448     for (uint j = 0; j < layer.nrows; j++) {
01449         uint y = j * TILE_HEIGHT;
01450 
01451         for (uint i = 0; i < layer.ncols; i++) {
01452             uint x = i * TILE_WIDTH;
01453 
01454             // This seems the best place to apply the dissolve because it
01455             // depends on the global position of each tile's
01456             // pixels. Apparently it's the only mode which can apply to a
01457             // single layer.
01458 
01459             if (layer.mode == DISSOLVE_MODE) {
01460                 if (layer.type == RGBA_GIMAGE)
01461                     dissolveRGBPixels(layer.image_tiles[j][i], x, y);
01462 
01463                 else if (layer.type == GRAYA_GIMAGE)
01464                     dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
01465             }
01466 
01467             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01468                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01469 
01470                     int m = x + k + layer.x_offset;
01471                     int n = y + l + layer.y_offset;
01472 
01473                     if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
01474                         continue;
01475 
01476                     (*merge)(layer, i, j, k, l, image, m, n);
01477                 }
01478             }
01479         }
01480     }
01481 }
01482 
01483 
01497 void XCFImageFormat::mergeRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
01498         QImage& image, int m, int n)
01499 {
01500     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01501     QRgb dst = image.pixel(m, n);
01502 
01503     uchar src_r = qRed(src);
01504     uchar src_g = qGreen(src);
01505     uchar src_b = qBlue(src);
01506     uchar src_a = qAlpha(src);
01507 
01508     uchar dst_r = qRed(dst);
01509     uchar dst_g = qGreen(dst);
01510     uchar dst_b = qBlue(dst);
01511     uchar dst_a = qAlpha(dst);
01512 
01513     if (!src_a) return; // nothing to merge
01514 
01515     switch (layer.mode) {
01516         case MULTIPLY_MODE: {
01517             src_r = INT_MULT(src_r, dst_r);
01518             src_g = INT_MULT(src_g, dst_g);
01519             src_b = INT_MULT(src_b, dst_b);
01520             src_a = qMin(src_a, dst_a);
01521             }
01522             break;
01523         case DIVIDE_MODE: {
01524             src_r = qMin((dst_r * 256) / (1 + src_r), 255);
01525             src_g = qMin((dst_g * 256) / (1 + src_g), 255);
01526             src_b = qMin((dst_b * 256) / (1 + src_b), 255);
01527             src_a = qMin(src_a, dst_a);
01528             }
01529             break;
01530         case SCREEN_MODE: {
01531             src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
01532             src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
01533             src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
01534             src_a = qMin(src_a, dst_a);
01535             }
01536             break;
01537         case OVERLAY_MODE: {
01538             src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
01539             src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
01540             src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
01541             src_a = qMin(src_a, dst_a);
01542             }
01543             break;
01544         case DIFFERENCE_MODE: {
01545             src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
01546             src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
01547             src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
01548             src_a = qMin(src_a, dst_a);
01549             }
01550             break;
01551         case ADDITION_MODE: {
01552               src_r = add_lut(dst_r,src_r);
01553               src_g = add_lut(dst_g,src_g);
01554               src_b = add_lut(dst_b,src_b);
01555               src_a = qMin(src_a, dst_a);
01556             }
01557             break;
01558         case SUBTRACT_MODE: {
01559             src_r = dst_r > src_r ? dst_r - src_r : 0;
01560             src_g = dst_g > src_g ? dst_g - src_g : 0;
01561             src_b = dst_b > src_b ? dst_b - src_b : 0;
01562             src_a = qMin(src_a, dst_a);
01563             }
01564             break;
01565         case DARKEN_ONLY_MODE: {
01566             src_r = dst_r < src_r ? dst_r : src_r;
01567             src_g = dst_g < src_g ? dst_g : src_g;
01568             src_b = dst_b < src_b ? dst_b : src_b;
01569             src_a = qMin( src_a, dst_a );
01570             }
01571             break;
01572         case LIGHTEN_ONLY_MODE: {
01573             src_r = dst_r < src_r ? src_r : dst_r;
01574             src_g = dst_g < src_g ? src_g : dst_g;
01575             src_b = dst_b < src_b ? src_b : dst_b;
01576             src_a = qMin(src_a, dst_a);
01577             }
01578             break;
01579         case HUE_MODE: {
01580             uchar new_r = dst_r;
01581             uchar new_g = dst_g;
01582             uchar new_b = dst_b;
01583 
01584             RGBTOHSV(src_r, src_g, src_b);
01585             RGBTOHSV(new_r, new_g, new_b);
01586 
01587             new_r = src_r;
01588 
01589             HSVTORGB(new_r, new_g, new_b);
01590 
01591             src_r = new_r;
01592             src_g = new_g;
01593             src_b = new_b;
01594             src_a = qMin( src_a, dst_a );
01595             }
01596             break;
01597         case SATURATION_MODE: {
01598             uchar new_r = dst_r;
01599             uchar new_g = dst_g;
01600             uchar new_b = dst_b;
01601 
01602             RGBTOHSV(src_r, src_g, src_b);
01603             RGBTOHSV(new_r, new_g, new_b);
01604 
01605             new_g = src_g;
01606 
01607             HSVTORGB(new_r, new_g, new_b);
01608 
01609             src_r = new_r;
01610             src_g = new_g;
01611             src_b = new_b;
01612             src_a = qMin(src_a, dst_a);
01613             }
01614             break;
01615         case VALUE_MODE: {
01616             uchar new_r = dst_r;
01617             uchar new_g = dst_g;
01618             uchar new_b = dst_b;
01619 
01620             RGBTOHSV(src_r, src_g, src_b);
01621             RGBTOHSV(new_r, new_g, new_b);
01622 
01623             new_b = src_b;
01624 
01625             HSVTORGB(new_r, new_g, new_b);
01626 
01627             src_r = new_r;
01628             src_g = new_g;
01629             src_b = new_b;
01630             src_a = qMin(src_a, dst_a);
01631             }
01632             break;
01633         case COLOR_MODE: {
01634             uchar new_r = dst_r;
01635             uchar new_g = dst_g;
01636             uchar new_b = dst_b;
01637 
01638             RGBTOHLS(src_r, src_g, src_b);
01639             RGBTOHLS(new_r, new_g, new_b);
01640 
01641             new_r = src_r;
01642             new_b = src_b;
01643 
01644             HLSTORGB(new_r, new_g, new_b);
01645 
01646             src_r = new_r;
01647             src_g = new_g;
01648             src_b = new_b;
01649             src_a = qMin(src_a, dst_a);
01650             }
01651             break;
01652         case DODGE_MODE: {
01653             uint tmp;
01654 
01655             tmp = dst_r << 8;
01656             tmp /= 256 - src_r;
01657             src_r = (uchar) qMin(tmp, 255u);
01658 
01659             tmp = dst_g << 8;
01660             tmp /= 256 - src_g;
01661             src_g = (uchar) qMin(tmp, 255u);
01662 
01663             tmp = dst_b << 8;
01664             tmp /= 256 - src_b;
01665             src_b = (uchar) qMin(tmp, 255u);
01666 
01667             src_a = qMin(src_a, dst_a);
01668             }
01669             break;
01670         case BURN_MODE: {
01671             uint tmp;
01672 
01673             tmp = (255 - dst_r) << 8;
01674             tmp /= src_r + 1;
01675             src_r = (uchar) qMin(tmp, 255u);
01676             src_r = 255 - src_r;
01677 
01678             tmp = (255 - dst_g) << 8;
01679             tmp /= src_g + 1;
01680             src_g = (uchar) qMin(tmp, 255u);
01681             src_g = 255 - src_g;
01682 
01683             tmp = (255 - dst_b) << 8;
01684             tmp /= src_b + 1;
01685             src_b = (uchar) qMin(tmp, 255u);
01686             src_b = 255 - src_b;
01687 
01688             src_a = qMin(src_a, dst_a);
01689             }
01690             break;
01691         case HARDLIGHT_MODE: {
01692             uint tmp;
01693             if (src_r > 128) {
01694                 tmp = ((int)255-dst_r) * ((int) 255 - ((src_r-128) << 1));
01695                 src_r = (uchar) qMin(255 - (tmp >> 8), 255u);
01696             } else {
01697                 tmp = (int) dst_r * ((int) src_r << 1);
01698                 src_r = (uchar) qMin(tmp >> 8, 255u);
01699             }
01700 
01701             if (src_g > 128) {
01702                 tmp = ((int)255-dst_g) * ((int) 255 - ((src_g-128) << 1));
01703                 src_g = (uchar) qMin(255 - (tmp >> 8), 255u);
01704             } else {
01705                 tmp = (int) dst_g * ((int) src_g << 1);
01706                 src_g = (uchar) qMin(tmp >> 8, 255u);
01707             }
01708 
01709             if (src_b > 128) {
01710                 tmp = ((int)255-dst_b) * ((int) 255 - ((src_b-128) << 1));
01711                 src_b = (uchar) qMin(255 - (tmp >> 8), 255u);
01712             } else {
01713                 tmp = (int) dst_b * ((int) src_b << 1);
01714                 src_b = (uchar) qMin(tmp >> 8, 255u);
01715             }
01716             src_a = qMin(src_a, dst_a);
01717             }
01718             break;
01719         case SOFTLIGHT_MODE: {
01720             uint tmpS, tmpM;
01721 
01722             tmpM = INT_MULT(dst_r, src_r);
01723             tmpS = 255 - INT_MULT((255 - dst_r), (255-src_r));
01724             src_r = INT_MULT((255 - dst_r), tmpM)
01725                 + INT_MULT(dst_r, tmpS);
01726 
01727             tmpM = INT_MULT(dst_g, src_g);
01728             tmpS = 255 - INT_MULT((255 - dst_g), (255-src_g));
01729             src_g = INT_MULT((255 - dst_g), tmpM)
01730                 + INT_MULT(dst_g, tmpS);
01731 
01732             tmpM = INT_MULT(dst_b, src_b);
01733             tmpS = 255 - INT_MULT((255 - dst_b), (255-src_b));
01734             src_b = INT_MULT((255 - dst_b), tmpM)
01735                 + INT_MULT(dst_b, tmpS);
01736 
01737             src_a = qMin(src_a, dst_a);
01738             }
01739             break;
01740         case GRAIN_EXTRACT_MODE: {
01741             int tmp;
01742             
01743             tmp = dst_r - src_r + 128;
01744             tmp = qMin(tmp, 255);
01745             tmp = qMax(tmp, 0);
01746             src_r = (uchar) tmp;
01747 
01748             tmp = dst_g - src_g + 128;
01749             tmp = qMin(tmp, 255);
01750             tmp = qMax(tmp, 0);
01751             src_g = (uchar) tmp;
01752 
01753             tmp = dst_b - src_b + 128;
01754             tmp = qMin(tmp, 255);
01755             tmp = qMax(tmp, 0);
01756             src_b = (uchar) tmp;
01757 
01758             src_a = qMin(src_a, dst_a);
01759             }
01760             break;
01761         case GRAIN_MERGE_MODE: {
01762             int tmp;
01763             
01764             tmp = dst_r + src_r - 128;
01765             tmp = qMin(tmp, 255);
01766             tmp = qMax(tmp, 0);
01767             src_r = (uchar) tmp;
01768 
01769             tmp = dst_g + src_g - 128;
01770             tmp = qMin(tmp, 255);
01771             tmp = qMax(tmp, 0);
01772             src_g = (uchar) tmp;
01773 
01774             tmp = dst_b + src_b - 128;
01775             tmp = qMin(tmp, 255);
01776             tmp = qMax(tmp, 0);
01777             src_b = (uchar) tmp;
01778 
01779             src_a = qMin(src_a, dst_a);
01780             }
01781             break;
01782     }
01783 
01784     src_a = INT_MULT(src_a, layer.opacity);
01785 
01786     // Apply the mask (if any)
01787 
01788     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01789             layer.mask_tiles[j].size() > (int)i)
01790         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01791 
01792     uchar new_r, new_g, new_b, new_a;
01793     new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
01794 
01795     float src_ratio = (float)src_a / new_a;
01796     float dst_ratio = 1.0 - src_ratio;
01797 
01798     new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
01799     new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
01800     new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
01801 
01802     if (!layer_modes[layer.mode].affect_alpha)
01803         new_a = dst_a;
01804 
01805     image.setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
01806 }
01807 
01808 
01820 void XCFImageFormat::mergeGrayToGray(Layer& layer, uint i, uint j, int k, int l,
01821         QImage& image, int m, int n)
01822 {
01823     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01824     image.setPixel(m, n, src);
01825 }
01826 
01827 
01839 void XCFImageFormat::mergeGrayAToGray(Layer& layer, uint i, uint j, int k, int l,
01840         QImage& image, int m, int n)
01841 {
01842     int src = qGray(layer.image_tiles[j][i].pixel(k, l));
01843     int dst = image.pixelIndex(m, n);
01844 
01845     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01846 
01847     if (!src_a) return; // nothing to merge
01848 
01849     switch (layer.mode) {
01850         case MULTIPLY_MODE: {
01851                 src = INT_MULT( src, dst );
01852             }
01853             break;
01854         case DIVIDE_MODE: {
01855                 src = qMin((dst * 256) / (1 + src), 255);
01856             }
01857             break;
01858         case SCREEN_MODE: {
01859                 src = 255 - INT_MULT(255 - dst, 255 - src);
01860             }
01861             break;
01862         case OVERLAY_MODE: {
01863                 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
01864             }
01865             break;
01866         case DIFFERENCE_MODE: {
01867                 src = dst > src ? dst - src : src - dst;
01868             }
01869             break;
01870         case ADDITION_MODE: {
01871                 src = add_lut(dst,src);
01872             }
01873             break;
01874         case SUBTRACT_MODE: {
01875                 src = dst > src ? dst - src : 0;
01876             }
01877             break;
01878         case DARKEN_ONLY_MODE: {
01879                 src = dst < src ? dst : src;
01880             }
01881             break;
01882         case LIGHTEN_ONLY_MODE: {
01883                 src = dst < src ? src : dst;
01884             }
01885             break;
01886         case DODGE_MODE: {
01887                 uint tmp = dst << 8;
01888                 tmp /= 256 - src;
01889                 src = (uchar) qMin(tmp, 255u);
01890             }
01891             break;
01892         case BURN_MODE: {
01893                 uint tmp = (255-dst) << 8;
01894                 tmp /= src + 1;
01895                 src = (uchar) qMin(tmp, 255u);
01896                 src = 255 - src;
01897             }
01898             break;
01899         case HARDLIGHT_MODE: {
01900                 uint tmp;
01901                 if (src > 128) {
01902                     tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1));
01903                     src = (uchar) qMin(255 - (tmp >> 8), 255u);
01904                 } else {
01905                     tmp = (int) dst * ((int) src << 1);
01906                     src = (uchar) qMin(tmp >> 8, 255u);
01907                 }
01908             }
01909             break;
01910         case SOFTLIGHT_MODE: {
01911                 uint tmpS, tmpM;
01912 
01913                 tmpM = INT_MULT(dst, src);
01914                 tmpS = 255 - INT_MULT((255-dst), (255-src));
01915                 src = INT_MULT((255 - dst), tmpM)
01916                     + INT_MULT(dst, tmpS);
01917 
01918             }
01919             break;
01920         case GRAIN_EXTRACT_MODE: {
01921                 int tmp;
01922                 
01923                 tmp = dst - src + 128;
01924                 tmp = qMin(tmp, 255);
01925                 tmp = qMax(tmp, 0);
01926 
01927                 src = (uchar) tmp;
01928             }
01929             break;
01930         case GRAIN_MERGE_MODE: {
01931                 int tmp;
01932                 
01933                 tmp = dst + src - 128;
01934                 tmp = qMin(tmp, 255);
01935                 tmp = qMax(tmp, 0);
01936 
01937                 src = (uchar) tmp;
01938             }
01939             break;
01940     }
01941 
01942     src_a = INT_MULT(src_a, layer.opacity);
01943 
01944     // Apply the mask (if any)
01945 
01946     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01947             layer.mask_tiles[j].size() > (int)i)
01948         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01949 
01950     uchar new_a = OPAQUE_OPACITY;
01951 
01952     float src_ratio = (float)src_a / new_a;
01953     float dst_ratio = 1.0 - src_ratio;
01954 
01955     uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
01956 
01957     image.setPixel(m, n, new_g);
01958 }
01959 
01960 
01974 void XCFImageFormat::mergeGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
01975         QImage& image, int m, int n)
01976 {
01977     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01978     uchar src_a = layer.opacity;
01979     image.setPixel(m, n, qRgba(src, src_a));
01980 }
01981 
01982 
01996 void XCFImageFormat::mergeGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
01997         QImage& image, int m, int n)
01998 {
01999     int src = qGray(layer.image_tiles[j][i].pixel(k, l));
02000     int dst = qGray(image.pixel(m, n));
02001 
02002     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02003     uchar dst_a = qAlpha(image.pixel(m, n));
02004 
02005     if (!src_a) return; // nothing to merge
02006 
02007     switch (layer.mode) {
02008         case MULTIPLY_MODE: {
02009                 src = INT_MULT(src, dst);
02010                 src_a = qMin(src_a, dst_a);
02011             }
02012             break;
02013         case DIVIDE_MODE: {
02014                 src = qMin((dst * 256) / (1 + src), 255);
02015                 src_a = qMin(src_a, dst_a);
02016             }
02017             break;
02018         case SCREEN_MODE: {
02019                 src = 255 - INT_MULT(255 - dst, 255 - src);
02020                 src_a = qMin(src_a, dst_a);
02021             }
02022             break;
02023         case OVERLAY_MODE: {
02024                 src = INT_MULT( dst, dst + INT_MULT(2 * src, 255 - dst));
02025                 src_a = qMin(src_a, dst_a);
02026             }
02027             break;
02028         case DIFFERENCE_MODE: {
02029                 src = dst > src ? dst - src : src - dst;
02030                 src_a = qMin(src_a, dst_a);
02031             }
02032             break;
02033         case ADDITION_MODE: {
02034                 src = add_lut(dst,src);
02035                 src_a = qMin(src_a, dst_a);
02036             }
02037             break;
02038         case SUBTRACT_MODE: {
02039                 src = dst > src ? dst - src : 0;
02040                 src_a = qMin(src_a, dst_a);
02041             }
02042             break;
02043         case DARKEN_ONLY_MODE: {
02044                 src = dst < src ? dst : src;
02045                 src_a = qMin(src_a, dst_a);
02046             }
02047             break;
02048         case LIGHTEN_ONLY_MODE: {
02049                 src = dst < src ? src : dst;
02050                 src_a = qMin(src_a, dst_a);
02051             }
02052             break;
02053         case DODGE_MODE: {
02054                 uint tmp = dst << 8;
02055                 tmp /= 256 - src;
02056                 src = (uchar) qMin(tmp, 255u);
02057                 src_a = qMin(src_a, dst_a);
02058             }
02059             break;
02060         case BURN_MODE: {
02061                 uint tmp = (255-dst) << 8;
02062                 tmp /= src + 1;
02063                 src = (uchar) qMin(tmp, 255u);
02064                 src = 255 - src;
02065                 src_a = qMin(src_a, dst_a);
02066             }
02067             break;
02068         case HARDLIGHT_MODE: {
02069                 uint tmp;
02070                 if (src > 128) {
02071                     tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1));
02072                     src = (uchar) qMin(255 - (tmp >> 8), 255u);
02073                 } else {
02074                     tmp = (int) dst * ((int) src << 1);
02075                     src = (uchar) qMin(tmp >> 8, 255u);
02076                 }
02077                 src_a = qMin(src_a, dst_a);
02078             }
02079             break;
02080         case SOFTLIGHT_MODE: {
02081                 uint tmpS, tmpM;
02082 
02083                 tmpM = INT_MULT(dst, src);
02084                 tmpS = 255 - INT_MULT((255 - dst), (255-src));
02085                 src = INT_MULT((255 - dst), tmpM)
02086                     + INT_MULT(dst, tmpS);
02087 
02088                 src_a = qMin(src_a, dst_a);
02089             }
02090             break;
02091         case GRAIN_EXTRACT_MODE: {
02092                 int tmp;
02093                 
02094                 tmp = dst - src + 128;
02095                 tmp = qMin(tmp, 255);
02096                 tmp = qMax(tmp, 0);
02097 
02098                 src = (uchar) tmp;
02099                 src_a = qMin(src_a, dst_a);
02100             }
02101             break;
02102         case GRAIN_MERGE_MODE: {
02103                 int tmp;
02104                 
02105                 tmp = dst + src - 128;
02106                 tmp = qMin(tmp, 255);
02107                 tmp = qMax(tmp, 0);
02108 
02109                 src = (uchar) tmp;
02110                 src_a = qMin(src_a, dst_a);
02111             }
02112             break;
02113     }
02114 
02115     src_a = INT_MULT(src_a, layer.opacity);
02116 
02117     // Apply the mask (if any)
02118     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02119             layer.mask_tiles[j].size() > (int)i)
02120         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02121 
02122     uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
02123 
02124     float src_ratio = (float)src_a / new_a;
02125     float dst_ratio = 1.0 - src_ratio;
02126 
02127     uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
02128 
02129     if (!layer_modes[layer.mode].affect_alpha)
02130         new_a = dst_a;
02131 
02132     image.setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
02133 }
02134 
02135 
02147 void XCFImageFormat::mergeIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
02148         QImage& image, int m, int n)
02149 {
02150     int src = layer.image_tiles[j][i].pixelIndex(k, l);
02151     image.setPixel(m, n, src);
02152 }
02153 
02154 
02166 void XCFImageFormat::mergeIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
02167         QImage& image, int m, int n)
02168 {
02169     uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
02170     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02171     src_a = INT_MULT( src_a, layer.opacity );
02172 
02173     if ( layer.apply_mask == 1 &&
02174             layer.mask_tiles.size() > (int)j &&
02175             layer.mask_tiles[j].size() > (int)i)
02176         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02177 
02178     if (src_a > 127) {
02179         src++;
02180         image.setPixel(m, n, src);
02181     }
02182 }
02183 
02184 
02198 void XCFImageFormat::mergeIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
02199         QImage& image, int m, int n)
02200 {
02201     QRgb src = layer.image_tiles[j][i].pixel(k, l);
02202     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02203     src_a = INT_MULT(src_a, layer.opacity);
02204 
02205     // Apply the mask (if any)
02206     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02207             layer.mask_tiles[j].size() > (int)i)
02208         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02209 
02210     // This is what appears in the GIMP window
02211     if (src_a <= 127)
02212         src_a = 0;
02213     else
02214         src_a = OPAQUE_OPACITY;
02215 
02216     image.setPixel(m, n, qRgba(src, src_a));
02217 }
02218 
02219 
02227 void XCFImageFormat::dissolveRGBPixels ( QImage& image, int x, int y )
02228 {
02229     // The apparently spurious rand() calls are to wind the random
02230     // numbers up to the same point for each tile.
02231 
02232     for (int l = 0; l < image.height(); l++) {
02233         srand(random_table[( l + y ) % RANDOM_TABLE_SIZE]);
02234 
02235         for (int k = 0; k < x; k++)
02236             rand();
02237 
02238         for (int k = 0; k < image.width(); k++) {
02239             int rand_val = rand() & 0xff;
02240             QRgb pixel = image.pixel(k, l);
02241 
02242             if (rand_val > qAlpha(pixel)) {
02243                 image.setPixel(k, l, qRgba(pixel, 0));
02244             }
02245         }
02246     }
02247 }
02248 
02249 
02259 void XCFImageFormat::dissolveAlphaPixels ( QImage& image, int x, int y )
02260 {
02261     // The apparently spurious rand() calls are to wind the random
02262     // numbers up to the same point for each tile.
02263 
02264     for (int l = 0; l < image.height(); l++) {
02265         srand( random_table[(l + y) % RANDOM_TABLE_SIZE]);
02266 
02267         for (int k = 0; k < x; k++)
02268             rand();
02269 
02270         for (int k = 0; k < image.width(); k++) {
02271             int rand_val = rand() & 0xff;
02272             uchar alpha = image.pixelIndex(k, l);
02273 
02274             if (rand_val > alpha) {
02275                 image.setPixel(k, l, 0);
02276             }
02277         }
02278     }
02279 }
02280 
02281 
02283 
02284 XCFHandler::XCFHandler()
02285 {
02286 }
02287 
02288 bool XCFHandler::canRead() const
02289 {
02290     if (canRead(device())) {
02291         setFormat("xcf");
02292         return true;
02293     }
02294     return false;
02295 }
02296 
02297 bool XCFHandler::read(QImage *image)
02298 {
02299     XCFImageFormat xcfif;
02300     return xcfif.readXCF(device(), image);
02301 }
02302 
02303 bool XCFHandler::write(const QImage &)
02304 {
02305     return false;
02306 }
02307 
02308 QByteArray XCFHandler::name() const
02309 {
02310     return "xcf";
02311 }
02312 
02313 bool XCFHandler::canRead(QIODevice *device)
02314 {
02315       if (!device) {
02316         qWarning("DDSHandler::canRead() called with no device");
02317         return false;
02318     }
02319 
02320     qint64 oldPos = device->pos();
02321 
02322     char head[8];
02323     qint64 readBytes = device->read(head, sizeof(head));
02324     if (readBytes != sizeof(head)) {
02325         if (device->isSequential()) {
02326             while (readBytes > 0)
02327                 device->ungetChar(head[readBytes-- - 1]);
02328         } else {
02329             device->seek(oldPos);
02330         }
02331         return false;
02332     }
02333 
02334     if (device->isSequential()) {
02335         while (readBytes > 0)
02336             device->ungetChar(head[readBytes-- - 1]);
02337     } else {
02338         device->seek(oldPos);
02339     }
02340 
02341     return qstrncmp(head, "gimp xcf", 8) == 0;
02342 }
02343 
02344 
02345 class XCFPlugin : public QImageIOPlugin
02346 {
02347 public:
02348     QStringList keys() const;
02349     Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
02350     QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
02351 };
02352 
02353 QStringList XCFPlugin::keys() const
02354 {
02355     return QStringList() << "xcf" << "XCF";
02356 }
02357 
02358 QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
02359 {
02360     if (format == "xcf" || format == "XCF")
02361         return Capabilities(CanRead);
02362     if (!format.isEmpty())
02363         return 0;
02364     if (!device->isOpen())
02365         return 0;
02366 
02367     Capabilities cap;
02368     if (device->isReadable() && XCFHandler::canRead(device))
02369         cap |= CanRead;
02370     return cap;
02371 }
02372 
02373 QImageIOHandler *XCFPlugin::create(QIODevice *device, const QByteArray &format) const
02374 {
02375     QImageIOHandler *handler = new XCFHandler;
02376     handler->setDevice(device);
02377     handler->setFormat(format);
02378     return handler;
02379 }
02380 
02381 Q_EXPORT_STATIC_PLUGIN(XCFPlugin)
02382 Q_EXPORT_PLUGIN2(xcf,XCFPlugin)

KImgIO

Skip menu "KImgIO"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal