• Skip to content
  • Skip to link menu
KDE 4.2 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     if (!copy) {
01182         return;
01183     }
01184 
01185     // For each tile...
01186 
01187     for (uint j = 0; j < layer.nrows; j++) {
01188         uint y = j * TILE_HEIGHT;
01189 
01190         for (uint i = 0; i < layer.ncols; i++) {
01191             uint x = i * TILE_WIDTH;
01192 
01193             // This seems the best place to apply the dissolve because it
01194             // depends on the global position of each tile's
01195             // pixels. Apparently it's the only mode which can apply to a
01196             // single layer.
01197 
01198             if (layer.mode == DISSOLVE_MODE) {
01199                 if (layer.type == RGBA_GIMAGE)
01200                     dissolveRGBPixels(layer.image_tiles[j][i], x, y);
01201 
01202                 else if (layer.type == GRAYA_GIMAGE)
01203                     dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
01204             }
01205 
01206             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01207                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01208 
01209                     int m = x + k + layer.x_offset;
01210                     int n = y + l + layer.y_offset;
01211 
01212                     if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
01213                         continue;
01214 
01215                     (*copy)(layer, i, j, k, l, image, m, n);
01216                 }
01217             }
01218         }
01219     }
01220 }
01221 
01222 
01236 void XCFImageFormat::copyRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
01237         QImage& image, int m, int n)
01238 {
01239     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01240     uchar src_a = layer.opacity;
01241 
01242     if (layer.type == RGBA_GIMAGE)
01243         src_a = INT_MULT(src_a, qAlpha(src));
01244 
01245     // Apply the mask (if any)
01246 
01247     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01248             layer.mask_tiles[j].size() > (int)i)
01249         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01250 
01251     image.setPixel(m, n, qRgba(src, src_a));
01252 }
01253 
01254 
01266 void XCFImageFormat::copyGrayToGray(Layer& layer, uint i, uint j, int k, int l,
01267         QImage& image, int m, int n)
01268 {
01269     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01270     image.setPixel(m, n, src);
01271 }
01272 
01273 
01287 void XCFImageFormat::copyGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
01288         QImage& image, int m, int n)
01289 {
01290     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01291     uchar src_a = layer.opacity;
01292     image.setPixel(m, n, qRgba(src, src_a));
01293 }
01294 
01295 
01309 void XCFImageFormat::copyGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
01310                       QImage& image, int m, int n)
01311 {
01312     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01313     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01314     src_a = INT_MULT(src_a, layer.opacity);
01315 
01316     // Apply the mask (if any)
01317 
01318     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01319             layer.mask_tiles[j].size() > (int)i)
01320         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01321 
01322     image.setPixel(m, n, qRgba(src, src_a));
01323 }
01324 
01325 
01337 void XCFImageFormat::copyIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
01338         QImage& image, int m, int n)
01339 {
01340     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01341     image.setPixel(m, n, src);
01342 }
01343 
01344 
01356 void XCFImageFormat::copyIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
01357         QImage& image, int m, int n)
01358 {
01359     uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
01360     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01361     src_a = INT_MULT(src_a, layer.opacity);
01362 
01363     if (layer.apply_mask == 1 &&
01364             layer.mask_tiles.size() > (int)j &&
01365             layer.mask_tiles[j].size() > (int)i)
01366         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01367 
01368     if (src_a > 127)
01369         src++;
01370     else
01371         src = 0;
01372 
01373 image.setPixel(m, n, src);
01374 }
01375 
01376 
01390 void XCFImageFormat::copyIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
01391         QImage& image, int m, int n)
01392 {
01393     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01394     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01395     src_a = INT_MULT(src_a, layer.opacity);
01396 
01397     // Apply the mask (if any)
01398     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01399             layer.mask_tiles[j].size() > (int)i)
01400         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01401 
01402     // This is what appears in the GIMP window
01403     if (src_a <= 127)
01404         src_a = 0;
01405     else
01406         src_a = OPAQUE_OPACITY;
01407 
01408     image.setPixel(m, n, qRgba(src, src_a));
01409 }
01410 
01411 
01416 void XCFImageFormat::mergeLayerIntoImage(XCFImage& xcf_image)
01417 {
01418     Layer& layer(xcf_image.layer);
01419     QImage& image(xcf_image.image);
01420 
01421     PixelMergeOperation merge = 0;
01422 
01423     if (!layer.opacity) return; // don't bother doing anything
01424 
01425     switch (layer.type) {
01426         case RGB_GIMAGE:
01427         case RGBA_GIMAGE:
01428             merge = mergeRGBToRGB;
01429             break;
01430         case GRAY_GIMAGE:
01431             if (layer.opacity == OPAQUE_OPACITY)
01432                 merge = mergeGrayToGray;
01433             else
01434                 merge = mergeGrayToRGB;
01435             break;
01436         case GRAYA_GIMAGE:
01437             if (xcf_image.image.depth() <= 8)
01438                 merge = mergeGrayAToGray;
01439             else
01440                 merge = mergeGrayAToRGB;
01441             break;
01442         case INDEXED_GIMAGE:
01443             merge = mergeIndexedToIndexed;
01444             break;
01445         case INDEXEDA_GIMAGE:
01446             if (xcf_image.image.depth() <= 8)
01447                 merge = mergeIndexedAToIndexed;
01448             else
01449                 merge = mergeIndexedAToRGB;
01450     }
01451 
01452     if (!merge) {
01453         return;
01454     }
01455 
01456     for (uint j = 0; j < layer.nrows; j++) {
01457         uint y = j * TILE_HEIGHT;
01458 
01459         for (uint i = 0; i < layer.ncols; i++) {
01460             uint x = i * TILE_WIDTH;
01461 
01462             // This seems the best place to apply the dissolve because it
01463             // depends on the global position of each tile's
01464             // pixels. Apparently it's the only mode which can apply to a
01465             // single layer.
01466 
01467             if (layer.mode == DISSOLVE_MODE) {
01468                 if (layer.type == RGBA_GIMAGE)
01469                     dissolveRGBPixels(layer.image_tiles[j][i], x, y);
01470 
01471                 else if (layer.type == GRAYA_GIMAGE)
01472                     dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
01473             }
01474 
01475             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01476                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01477 
01478                     int m = x + k + layer.x_offset;
01479                     int n = y + l + layer.y_offset;
01480 
01481                     if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
01482                         continue;
01483 
01484                     (*merge)(layer, i, j, k, l, image, m, n);
01485                 }
01486             }
01487         }
01488     }
01489 }
01490 
01491 
01505 void XCFImageFormat::mergeRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
01506         QImage& image, int m, int n)
01507 {
01508     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01509     QRgb dst = image.pixel(m, n);
01510 
01511     uchar src_r = qRed(src);
01512     uchar src_g = qGreen(src);
01513     uchar src_b = qBlue(src);
01514     uchar src_a = qAlpha(src);
01515 
01516     uchar dst_r = qRed(dst);
01517     uchar dst_g = qGreen(dst);
01518     uchar dst_b = qBlue(dst);
01519     uchar dst_a = qAlpha(dst);
01520 
01521     if (!src_a) return; // nothing to merge
01522 
01523     switch (layer.mode) {
01524         case MULTIPLY_MODE: {
01525             src_r = INT_MULT(src_r, dst_r);
01526             src_g = INT_MULT(src_g, dst_g);
01527             src_b = INT_MULT(src_b, dst_b);
01528             src_a = qMin(src_a, dst_a);
01529             }
01530             break;
01531         case DIVIDE_MODE: {
01532             src_r = qMin((dst_r * 256) / (1 + src_r), 255);
01533             src_g = qMin((dst_g * 256) / (1 + src_g), 255);
01534             src_b = qMin((dst_b * 256) / (1 + src_b), 255);
01535             src_a = qMin(src_a, dst_a);
01536             }
01537             break;
01538         case SCREEN_MODE: {
01539             src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
01540             src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
01541             src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
01542             src_a = qMin(src_a, dst_a);
01543             }
01544             break;
01545         case OVERLAY_MODE: {
01546             src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
01547             src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
01548             src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
01549             src_a = qMin(src_a, dst_a);
01550             }
01551             break;
01552         case DIFFERENCE_MODE: {
01553             src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
01554             src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
01555             src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
01556             src_a = qMin(src_a, dst_a);
01557             }
01558             break;
01559         case ADDITION_MODE: {
01560               src_r = add_lut(dst_r,src_r);
01561               src_g = add_lut(dst_g,src_g);
01562               src_b = add_lut(dst_b,src_b);
01563               src_a = qMin(src_a, dst_a);
01564             }
01565             break;
01566         case SUBTRACT_MODE: {
01567             src_r = dst_r > src_r ? dst_r - src_r : 0;
01568             src_g = dst_g > src_g ? dst_g - src_g : 0;
01569             src_b = dst_b > src_b ? dst_b - src_b : 0;
01570             src_a = qMin(src_a, dst_a);
01571             }
01572             break;
01573         case DARKEN_ONLY_MODE: {
01574             src_r = dst_r < src_r ? dst_r : src_r;
01575             src_g = dst_g < src_g ? dst_g : src_g;
01576             src_b = dst_b < src_b ? dst_b : src_b;
01577             src_a = qMin( src_a, dst_a );
01578             }
01579             break;
01580         case LIGHTEN_ONLY_MODE: {
01581             src_r = dst_r < src_r ? src_r : dst_r;
01582             src_g = dst_g < src_g ? src_g : dst_g;
01583             src_b = dst_b < src_b ? src_b : dst_b;
01584             src_a = qMin(src_a, dst_a);
01585             }
01586             break;
01587         case HUE_MODE: {
01588             uchar new_r = dst_r;
01589             uchar new_g = dst_g;
01590             uchar new_b = dst_b;
01591 
01592             RGBTOHSV(src_r, src_g, src_b);
01593             RGBTOHSV(new_r, new_g, new_b);
01594 
01595             new_r = src_r;
01596 
01597             HSVTORGB(new_r, new_g, new_b);
01598 
01599             src_r = new_r;
01600             src_g = new_g;
01601             src_b = new_b;
01602             src_a = qMin( src_a, dst_a );
01603             }
01604             break;
01605         case SATURATION_MODE: {
01606             uchar new_r = dst_r;
01607             uchar new_g = dst_g;
01608             uchar new_b = dst_b;
01609 
01610             RGBTOHSV(src_r, src_g, src_b);
01611             RGBTOHSV(new_r, new_g, new_b);
01612 
01613             new_g = src_g;
01614 
01615             HSVTORGB(new_r, new_g, new_b);
01616 
01617             src_r = new_r;
01618             src_g = new_g;
01619             src_b = new_b;
01620             src_a = qMin(src_a, dst_a);
01621             }
01622             break;
01623         case VALUE_MODE: {
01624             uchar new_r = dst_r;
01625             uchar new_g = dst_g;
01626             uchar new_b = dst_b;
01627 
01628             RGBTOHSV(src_r, src_g, src_b);
01629             RGBTOHSV(new_r, new_g, new_b);
01630 
01631             new_b = src_b;
01632 
01633             HSVTORGB(new_r, new_g, new_b);
01634 
01635             src_r = new_r;
01636             src_g = new_g;
01637             src_b = new_b;
01638             src_a = qMin(src_a, dst_a);
01639             }
01640             break;
01641         case COLOR_MODE: {
01642             uchar new_r = dst_r;
01643             uchar new_g = dst_g;
01644             uchar new_b = dst_b;
01645 
01646             RGBTOHLS(src_r, src_g, src_b);
01647             RGBTOHLS(new_r, new_g, new_b);
01648 
01649             new_r = src_r;
01650             new_b = src_b;
01651 
01652             HLSTORGB(new_r, new_g, new_b);
01653 
01654             src_r = new_r;
01655             src_g = new_g;
01656             src_b = new_b;
01657             src_a = qMin(src_a, dst_a);
01658             }
01659             break;
01660         case DODGE_MODE: {
01661             uint tmp;
01662 
01663             tmp = dst_r << 8;
01664             tmp /= 256 - src_r;
01665             src_r = (uchar) qMin(tmp, 255u);
01666 
01667             tmp = dst_g << 8;
01668             tmp /= 256 - src_g;
01669             src_g = (uchar) qMin(tmp, 255u);
01670 
01671             tmp = dst_b << 8;
01672             tmp /= 256 - src_b;
01673             src_b = (uchar) qMin(tmp, 255u);
01674 
01675             src_a = qMin(src_a, dst_a);
01676             }
01677             break;
01678         case BURN_MODE: {
01679             uint tmp;
01680 
01681             tmp = (255 - dst_r) << 8;
01682             tmp /= src_r + 1;
01683             src_r = (uchar) qMin(tmp, 255u);
01684             src_r = 255 - src_r;
01685 
01686             tmp = (255 - dst_g) << 8;
01687             tmp /= src_g + 1;
01688             src_g = (uchar) qMin(tmp, 255u);
01689             src_g = 255 - src_g;
01690 
01691             tmp = (255 - dst_b) << 8;
01692             tmp /= src_b + 1;
01693             src_b = (uchar) qMin(tmp, 255u);
01694             src_b = 255 - src_b;
01695 
01696             src_a = qMin(src_a, dst_a);
01697             }
01698             break;
01699         case HARDLIGHT_MODE: {
01700             uint tmp;
01701             if (src_r > 128) {
01702                 tmp = ((int)255-dst_r) * ((int) 255 - ((src_r-128) << 1));
01703                 src_r = (uchar) qMin(255 - (tmp >> 8), 255u);
01704             } else {
01705                 tmp = (int) dst_r * ((int) src_r << 1);
01706                 src_r = (uchar) qMin(tmp >> 8, 255u);
01707             }
01708 
01709             if (src_g > 128) {
01710                 tmp = ((int)255-dst_g) * ((int) 255 - ((src_g-128) << 1));
01711                 src_g = (uchar) qMin(255 - (tmp >> 8), 255u);
01712             } else {
01713                 tmp = (int) dst_g * ((int) src_g << 1);
01714                 src_g = (uchar) qMin(tmp >> 8, 255u);
01715             }
01716 
01717             if (src_b > 128) {
01718                 tmp = ((int)255-dst_b) * ((int) 255 - ((src_b-128) << 1));
01719                 src_b = (uchar) qMin(255 - (tmp >> 8), 255u);
01720             } else {
01721                 tmp = (int) dst_b * ((int) src_b << 1);
01722                 src_b = (uchar) qMin(tmp >> 8, 255u);
01723             }
01724             src_a = qMin(src_a, dst_a);
01725             }
01726             break;
01727         case SOFTLIGHT_MODE: {
01728             uint tmpS, tmpM;
01729 
01730             tmpM = INT_MULT(dst_r, src_r);
01731             tmpS = 255 - INT_MULT((255 - dst_r), (255-src_r));
01732             src_r = INT_MULT((255 - dst_r), tmpM)
01733                 + INT_MULT(dst_r, tmpS);
01734 
01735             tmpM = INT_MULT(dst_g, src_g);
01736             tmpS = 255 - INT_MULT((255 - dst_g), (255-src_g));
01737             src_g = INT_MULT((255 - dst_g), tmpM)
01738                 + INT_MULT(dst_g, tmpS);
01739 
01740             tmpM = INT_MULT(dst_b, src_b);
01741             tmpS = 255 - INT_MULT((255 - dst_b), (255-src_b));
01742             src_b = INT_MULT((255 - dst_b), tmpM)
01743                 + INT_MULT(dst_b, tmpS);
01744 
01745             src_a = qMin(src_a, dst_a);
01746             }
01747             break;
01748         case GRAIN_EXTRACT_MODE: {
01749             int tmp;
01750             
01751             tmp = dst_r - src_r + 128;
01752             tmp = qMin(tmp, 255);
01753             tmp = qMax(tmp, 0);
01754             src_r = (uchar) tmp;
01755 
01756             tmp = dst_g - src_g + 128;
01757             tmp = qMin(tmp, 255);
01758             tmp = qMax(tmp, 0);
01759             src_g = (uchar) tmp;
01760 
01761             tmp = dst_b - src_b + 128;
01762             tmp = qMin(tmp, 255);
01763             tmp = qMax(tmp, 0);
01764             src_b = (uchar) tmp;
01765 
01766             src_a = qMin(src_a, dst_a);
01767             }
01768             break;
01769         case GRAIN_MERGE_MODE: {
01770             int tmp;
01771             
01772             tmp = dst_r + src_r - 128;
01773             tmp = qMin(tmp, 255);
01774             tmp = qMax(tmp, 0);
01775             src_r = (uchar) tmp;
01776 
01777             tmp = dst_g + src_g - 128;
01778             tmp = qMin(tmp, 255);
01779             tmp = qMax(tmp, 0);
01780             src_g = (uchar) tmp;
01781 
01782             tmp = dst_b + src_b - 128;
01783             tmp = qMin(tmp, 255);
01784             tmp = qMax(tmp, 0);
01785             src_b = (uchar) tmp;
01786 
01787             src_a = qMin(src_a, dst_a);
01788             }
01789             break;
01790     }
01791 
01792     src_a = INT_MULT(src_a, layer.opacity);
01793 
01794     // Apply the mask (if any)
01795 
01796     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01797             layer.mask_tiles[j].size() > (int)i)
01798         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01799 
01800     uchar new_r, new_g, new_b, new_a;
01801     new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
01802 
01803     float src_ratio = (float)src_a / new_a;
01804     float dst_ratio = 1.0 - src_ratio;
01805 
01806     new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
01807     new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
01808     new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
01809 
01810     if (!layer_modes[layer.mode].affect_alpha)
01811         new_a = dst_a;
01812 
01813     image.setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
01814 }
01815 
01816 
01828 void XCFImageFormat::mergeGrayToGray(Layer& layer, uint i, uint j, int k, int l,
01829         QImage& image, int m, int n)
01830 {
01831     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01832     image.setPixel(m, n, src);
01833 }
01834 
01835 
01847 void XCFImageFormat::mergeGrayAToGray(Layer& layer, uint i, uint j, int k, int l,
01848         QImage& image, int m, int n)
01849 {
01850     int src = qGray(layer.image_tiles[j][i].pixel(k, l));
01851     int dst = image.pixelIndex(m, n);
01852 
01853     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01854 
01855     if (!src_a) return; // nothing to merge
01856 
01857     switch (layer.mode) {
01858         case MULTIPLY_MODE: {
01859                 src = INT_MULT( src, dst );
01860             }
01861             break;
01862         case DIVIDE_MODE: {
01863                 src = qMin((dst * 256) / (1 + src), 255);
01864             }
01865             break;
01866         case SCREEN_MODE: {
01867                 src = 255 - INT_MULT(255 - dst, 255 - src);
01868             }
01869             break;
01870         case OVERLAY_MODE: {
01871                 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
01872             }
01873             break;
01874         case DIFFERENCE_MODE: {
01875                 src = dst > src ? dst - src : src - dst;
01876             }
01877             break;
01878         case ADDITION_MODE: {
01879                 src = add_lut(dst,src);
01880             }
01881             break;
01882         case SUBTRACT_MODE: {
01883                 src = dst > src ? dst - src : 0;
01884             }
01885             break;
01886         case DARKEN_ONLY_MODE: {
01887                 src = dst < src ? dst : src;
01888             }
01889             break;
01890         case LIGHTEN_ONLY_MODE: {
01891                 src = dst < src ? src : dst;
01892             }
01893             break;
01894         case DODGE_MODE: {
01895                 uint tmp = dst << 8;
01896                 tmp /= 256 - src;
01897                 src = (uchar) qMin(tmp, 255u);
01898             }
01899             break;
01900         case BURN_MODE: {
01901                 uint tmp = (255-dst) << 8;
01902                 tmp /= src + 1;
01903                 src = (uchar) qMin(tmp, 255u);
01904                 src = 255 - src;
01905             }
01906             break;
01907         case HARDLIGHT_MODE: {
01908                 uint tmp;
01909                 if (src > 128) {
01910                     tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1));
01911                     src = (uchar) qMin(255 - (tmp >> 8), 255u);
01912                 } else {
01913                     tmp = (int) dst * ((int) src << 1);
01914                     src = (uchar) qMin(tmp >> 8, 255u);
01915                 }
01916             }
01917             break;
01918         case SOFTLIGHT_MODE: {
01919                 uint tmpS, tmpM;
01920 
01921                 tmpM = INT_MULT(dst, src);
01922                 tmpS = 255 - INT_MULT((255-dst), (255-src));
01923                 src = INT_MULT((255 - dst), tmpM)
01924                     + INT_MULT(dst, tmpS);
01925 
01926             }
01927             break;
01928         case GRAIN_EXTRACT_MODE: {
01929                 int tmp;
01930                 
01931                 tmp = dst - src + 128;
01932                 tmp = qMin(tmp, 255);
01933                 tmp = qMax(tmp, 0);
01934 
01935                 src = (uchar) tmp;
01936             }
01937             break;
01938         case GRAIN_MERGE_MODE: {
01939                 int tmp;
01940                 
01941                 tmp = dst + src - 128;
01942                 tmp = qMin(tmp, 255);
01943                 tmp = qMax(tmp, 0);
01944 
01945                 src = (uchar) tmp;
01946             }
01947             break;
01948     }
01949 
01950     src_a = INT_MULT(src_a, layer.opacity);
01951 
01952     // Apply the mask (if any)
01953 
01954     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01955             layer.mask_tiles[j].size() > (int)i)
01956         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01957 
01958     uchar new_a = OPAQUE_OPACITY;
01959 
01960     float src_ratio = (float)src_a / new_a;
01961     float dst_ratio = 1.0 - src_ratio;
01962 
01963     uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
01964 
01965     image.setPixel(m, n, new_g);
01966 }
01967 
01968 
01982 void XCFImageFormat::mergeGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
01983         QImage& image, int m, int n)
01984 {
01985     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01986     uchar src_a = layer.opacity;
01987     image.setPixel(m, n, qRgba(src, src_a));
01988 }
01989 
01990 
02004 void XCFImageFormat::mergeGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
02005         QImage& image, int m, int n)
02006 {
02007     int src = qGray(layer.image_tiles[j][i].pixel(k, l));
02008     int dst = qGray(image.pixel(m, n));
02009 
02010     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02011     uchar dst_a = qAlpha(image.pixel(m, n));
02012 
02013     if (!src_a) return; // nothing to merge
02014 
02015     switch (layer.mode) {
02016         case MULTIPLY_MODE: {
02017                 src = INT_MULT(src, dst);
02018                 src_a = qMin(src_a, dst_a);
02019             }
02020             break;
02021         case DIVIDE_MODE: {
02022                 src = qMin((dst * 256) / (1 + src), 255);
02023                 src_a = qMin(src_a, dst_a);
02024             }
02025             break;
02026         case SCREEN_MODE: {
02027                 src = 255 - INT_MULT(255 - dst, 255 - src);
02028                 src_a = qMin(src_a, dst_a);
02029             }
02030             break;
02031         case OVERLAY_MODE: {
02032                 src = INT_MULT( dst, dst + INT_MULT(2 * src, 255 - dst));
02033                 src_a = qMin(src_a, dst_a);
02034             }
02035             break;
02036         case DIFFERENCE_MODE: {
02037                 src = dst > src ? dst - src : src - dst;
02038                 src_a = qMin(src_a, dst_a);
02039             }
02040             break;
02041         case ADDITION_MODE: {
02042                 src = add_lut(dst,src);
02043                 src_a = qMin(src_a, dst_a);
02044             }
02045             break;
02046         case SUBTRACT_MODE: {
02047                 src = dst > src ? dst - src : 0;
02048                 src_a = qMin(src_a, dst_a);
02049             }
02050             break;
02051         case DARKEN_ONLY_MODE: {
02052                 src = dst < src ? dst : src;
02053                 src_a = qMin(src_a, dst_a);
02054             }
02055             break;
02056         case LIGHTEN_ONLY_MODE: {
02057                 src = dst < src ? src : dst;
02058                 src_a = qMin(src_a, dst_a);
02059             }
02060             break;
02061         case DODGE_MODE: {
02062                 uint tmp = dst << 8;
02063                 tmp /= 256 - src;
02064                 src = (uchar) qMin(tmp, 255u);
02065                 src_a = qMin(src_a, dst_a);
02066             }
02067             break;
02068         case BURN_MODE: {
02069                 uint tmp = (255-dst) << 8;
02070                 tmp /= src + 1;
02071                 src = (uchar) qMin(tmp, 255u);
02072                 src = 255 - src;
02073                 src_a = qMin(src_a, dst_a);
02074             }
02075             break;
02076         case HARDLIGHT_MODE: {
02077                 uint tmp;
02078                 if (src > 128) {
02079                     tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1));
02080                     src = (uchar) qMin(255 - (tmp >> 8), 255u);
02081                 } else {
02082                     tmp = (int) dst * ((int) src << 1);
02083                     src = (uchar) qMin(tmp >> 8, 255u);
02084                 }
02085                 src_a = qMin(src_a, dst_a);
02086             }
02087             break;
02088         case SOFTLIGHT_MODE: {
02089                 uint tmpS, tmpM;
02090 
02091                 tmpM = INT_MULT(dst, src);
02092                 tmpS = 255 - INT_MULT((255 - dst), (255-src));
02093                 src = INT_MULT((255 - dst), tmpM)
02094                     + INT_MULT(dst, tmpS);
02095 
02096                 src_a = qMin(src_a, dst_a);
02097             }
02098             break;
02099         case GRAIN_EXTRACT_MODE: {
02100                 int tmp;
02101                 
02102                 tmp = dst - src + 128;
02103                 tmp = qMin(tmp, 255);
02104                 tmp = qMax(tmp, 0);
02105 
02106                 src = (uchar) tmp;
02107                 src_a = qMin(src_a, dst_a);
02108             }
02109             break;
02110         case GRAIN_MERGE_MODE: {
02111                 int tmp;
02112                 
02113                 tmp = dst + src - 128;
02114                 tmp = qMin(tmp, 255);
02115                 tmp = qMax(tmp, 0);
02116 
02117                 src = (uchar) tmp;
02118                 src_a = qMin(src_a, dst_a);
02119             }
02120             break;
02121     }
02122 
02123     src_a = INT_MULT(src_a, layer.opacity);
02124 
02125     // Apply the mask (if any)
02126     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02127             layer.mask_tiles[j].size() > (int)i)
02128         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02129 
02130     uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
02131 
02132     float src_ratio = (float)src_a / new_a;
02133     float dst_ratio = 1.0 - src_ratio;
02134 
02135     uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
02136 
02137     if (!layer_modes[layer.mode].affect_alpha)
02138         new_a = dst_a;
02139 
02140     image.setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
02141 }
02142 
02143 
02155 void XCFImageFormat::mergeIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
02156         QImage& image, int m, int n)
02157 {
02158     int src = layer.image_tiles[j][i].pixelIndex(k, l);
02159     image.setPixel(m, n, src);
02160 }
02161 
02162 
02174 void XCFImageFormat::mergeIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
02175         QImage& image, int m, int n)
02176 {
02177     uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
02178     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02179     src_a = INT_MULT( src_a, layer.opacity );
02180 
02181     if ( layer.apply_mask == 1 &&
02182             layer.mask_tiles.size() > (int)j &&
02183             layer.mask_tiles[j].size() > (int)i)
02184         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02185 
02186     if (src_a > 127) {
02187         src++;
02188         image.setPixel(m, n, src);
02189     }
02190 }
02191 
02192 
02206 void XCFImageFormat::mergeIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
02207         QImage& image, int m, int n)
02208 {
02209     QRgb src = layer.image_tiles[j][i].pixel(k, l);
02210     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02211     src_a = INT_MULT(src_a, layer.opacity);
02212 
02213     // Apply the mask (if any)
02214     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02215             layer.mask_tiles[j].size() > (int)i)
02216         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02217 
02218     // This is what appears in the GIMP window
02219     if (src_a <= 127)
02220         src_a = 0;
02221     else
02222         src_a = OPAQUE_OPACITY;
02223 
02224     image.setPixel(m, n, qRgba(src, src_a));
02225 }
02226 
02227 
02235 void XCFImageFormat::dissolveRGBPixels ( QImage& image, int x, int y )
02236 {
02237     // The apparently spurious rand() calls are to wind the random
02238     // numbers up to the same point for each tile.
02239 
02240     for (int l = 0; l < image.height(); l++) {
02241         srand(random_table[( l + y ) % RANDOM_TABLE_SIZE]);
02242 
02243         for (int k = 0; k < x; k++)
02244             rand();
02245 
02246         for (int k = 0; k < image.width(); k++) {
02247             int rand_val = rand() & 0xff;
02248             QRgb pixel = image.pixel(k, l);
02249 
02250             if (rand_val > qAlpha(pixel)) {
02251                 image.setPixel(k, l, qRgba(pixel, 0));
02252             }
02253         }
02254     }
02255 }
02256 
02257 
02267 void XCFImageFormat::dissolveAlphaPixels ( QImage& image, int x, int y )
02268 {
02269     // The apparently spurious rand() calls are to wind the random
02270     // numbers up to the same point for each tile.
02271 
02272     for (int l = 0; l < image.height(); l++) {
02273         srand( random_table[(l + y) % RANDOM_TABLE_SIZE]);
02274 
02275         for (int k = 0; k < x; k++)
02276             rand();
02277 
02278         for (int k = 0; k < image.width(); k++) {
02279             int rand_val = rand() & 0xff;
02280             uchar alpha = image.pixelIndex(k, l);
02281 
02282             if (rand_val > alpha) {
02283                 image.setPixel(k, l, 0);
02284             }
02285         }
02286     }
02287 }
02288 
02289 
02291 
02292 XCFHandler::XCFHandler()
02293 {
02294 }
02295 
02296 bool XCFHandler::canRead() const
02297 {
02298     if (canRead(device())) {
02299         setFormat("xcf");
02300         return true;
02301     }
02302     return false;
02303 }
02304 
02305 bool XCFHandler::read(QImage *image)
02306 {
02307     XCFImageFormat xcfif;
02308     return xcfif.readXCF(device(), image);
02309 }
02310 
02311 bool XCFHandler::write(const QImage &)
02312 {
02313     return false;
02314 }
02315 
02316 QByteArray XCFHandler::name() const
02317 {
02318     return "xcf";
02319 }
02320 
02321 bool XCFHandler::canRead(QIODevice *device)
02322 {
02323       if (!device) {
02324         qWarning("DDSHandler::canRead() called with no device");
02325         return false;
02326     }
02327 
02328     qint64 oldPos = device->pos();
02329 
02330     char head[8];
02331     qint64 readBytes = device->read(head, sizeof(head));
02332     if (readBytes != sizeof(head)) {
02333         if (device->isSequential()) {
02334             while (readBytes > 0)
02335                 device->ungetChar(head[readBytes-- - 1]);
02336         } else {
02337             device->seek(oldPos);
02338         }
02339         return false;
02340     }
02341 
02342     if (device->isSequential()) {
02343         while (readBytes > 0)
02344             device->ungetChar(head[readBytes-- - 1]);
02345     } else {
02346         device->seek(oldPos);
02347     }
02348 
02349     return qstrncmp(head, "gimp xcf", 8) == 0;
02350 }
02351 
02352 
02353 class XCFPlugin : public QImageIOPlugin
02354 {
02355 public:
02356     QStringList keys() const;
02357     Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
02358     QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
02359 };
02360 
02361 QStringList XCFPlugin::keys() const
02362 {
02363     return QStringList() << "xcf" << "XCF";
02364 }
02365 
02366 QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
02367 {
02368     if (format == "xcf" || format == "XCF")
02369         return Capabilities(CanRead);
02370     if (!format.isEmpty())
02371         return 0;
02372     if (!device->isOpen())
02373         return 0;
02374 
02375     Capabilities cap;
02376     if (device->isReadable() && XCFHandler::canRead(device))
02377         cap |= CanRead;
02378     return cap;
02379 }
02380 
02381 QImageIOHandler *XCFPlugin::create(QIODevice *device, const QByteArray &format) const
02382 {
02383     QImageIOHandler *handler = new XCFHandler;
02384     handler->setDevice(device);
02385     handler->setFormat(format);
02386     return handler;
02387 }
02388 
02389 Q_EXPORT_STATIC_PLUGIN(XCFPlugin)
02390 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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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