• Skip to content
  • Skip to link menu
KDE 4.3 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                 quint32 ncolors;
00280                 xcf_io >> ncolors;
00281 
00282                 if(size > 65535 || size < 4)
00283                     return false;
00284 
00285         size = 3 * ncolors + 4;
00286         data = new char[size];
00287 
00288                 // since we already read "ncolors" from the stream, we put that data back
00289                 data[0] = 0;
00290                 data[1] = 0;
00291                 data[2] = ncolors >> 8;
00292                 data[3] = ncolors & 255;
00293 
00294                 // ... and read the remaining bytes from the stream
00295         xcf_io.readRawData(data + 4, size - 4);
00296     } else if (type == PROP_USER_UNIT) {
00297         // The USER UNIT property size is not correct. I'm not sure why, though.
00298         float factor;
00299         qint32 digits;
00300 
00301         xcf_io >> size >> factor >> digits;
00302 
00303         for (int i = 0; i < 5; i++) {
00304             char* unit_strings;
00305 
00306             xcf_io >> unit_strings;
00307 
00308             delete[] unit_strings;
00309 
00310             if (xcf_io.device()->atEnd()) {
00311                 kDebug(399) << "XCF: read failure on property " << type;
00312                 return false;
00313             }
00314         }
00315 
00316         size = 0;
00317     } else {
00318                 xcf_io >> size;
00319                 if(size >256000)
00320                     return false;
00321                 data = new char[size];
00322         xcf_io.readRawData(data, size);
00323         }
00324 
00325     if (size != 0 && data)
00326             bytes = QByteArray(data,size);
00327     
00328         delete [] data;
00329 
00330     return true;
00331 }
00332 
00333 
00342 bool XCFImageFormat::loadLayer(QDataStream& xcf_io, XCFImage& xcf_image)
00343 {
00344     Layer& layer(xcf_image.layer);
00345     delete[] layer.name;
00346 
00347     xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
00348 
00349     if (!loadLayerProperties(xcf_io, layer))
00350         return false;
00351 #if 0
00352   cout << "layer: \"" << layer.name << "\", size: " << layer.width << " x "
00353        << layer.height << ", type: " << layer.type << ", mode: " << layer.mode
00354        << ", opacity: " << layer.opacity << ", visible: " << layer.visible
00355        << ", offset: " << layer.x_offset << ", " << layer.y_offset << endl;
00356 #endif
00357   // Skip reading the rest of it if it is not visible. Typically, when
00358   // you export an image from the The GIMP it flattens (or merges) only
00359   // the visible layers into the output image.
00360 
00361     if (layer.visible == 0)
00362         return true;
00363 
00364     // If there are any more layers, merge them into the final QImage.
00365 
00366     xcf_io >> layer.hierarchy_offset >> layer.mask_offset;
00367 
00368     // Allocate the individual tile QImages based on the size and type
00369     // of this layer.
00370 
00371     if( !composeTiles(xcf_image))
00372         return false;
00373     xcf_io.device()->seek(layer.hierarchy_offset);
00374 
00375     // As tiles are loaded, they are copied into the layers tiles by
00376     // this routine. (loadMask(), below, uses a slightly different
00377     // version of assignBytes().)
00378 
00379     layer.assignBytes = assignImageBytes;
00380 
00381     if (!loadHierarchy(xcf_io, layer))
00382         return false;
00383 
00384     if (layer.mask_offset != 0) {
00385         xcf_io.device()->seek(layer.mask_offset);
00386 
00387         if (!loadMask(xcf_io, layer))
00388             return false;
00389     }
00390 
00391     // Now we should have enough information to initialize the final
00392     // QImage. The first visible layer determines the attributes
00393     // of the QImage.
00394 
00395     if (!xcf_image.initialized) {
00396         if( !initializeImage(xcf_image))
00397             return false;
00398         copyLayerToImage(xcf_image);
00399         xcf_image.initialized = true;
00400     } else
00401         mergeLayerIntoImage(xcf_image);
00402 
00403     return true;
00404 }
00405 
00406 
00414 bool XCFImageFormat::loadLayerProperties(QDataStream& xcf_io, Layer& layer)
00415 {
00416     while (true) {
00417         PropType type;
00418         QByteArray bytes;
00419 
00420         if (!loadProperty(xcf_io, type, bytes)) {
00421             kDebug(399) << "XCF: error loading layer properties";
00422             return false;
00423         }
00424 
00425         QDataStream property(bytes);
00426 
00427         switch (type) {
00428             case PROP_END:
00429                 return true;
00430 
00431             case PROP_ACTIVE_LAYER:
00432                 layer.active = true;
00433                 break;
00434 
00435             case PROP_OPACITY:
00436                 property >> layer.opacity;
00437                 break;
00438 
00439             case PROP_VISIBLE:
00440                 property >> layer.visible;
00441                 break;
00442 
00443             case PROP_LINKED:
00444                 property >> layer.linked;
00445                 break;
00446 
00447             case PROP_PRESERVE_TRANSPARENCY:
00448                 property >> layer.preserve_transparency;
00449                 break;
00450 
00451             case PROP_APPLY_MASK:
00452                 property >> layer.apply_mask;
00453                 break;
00454 
00455             case PROP_EDIT_MASK:
00456                 property >> layer.edit_mask;
00457                 break;
00458 
00459             case PROP_SHOW_MASK:
00460                 property >> layer.show_mask;
00461                 break;
00462 
00463             case PROP_OFFSETS:
00464                 property >> layer.x_offset >> layer.y_offset;
00465                 break;
00466 
00467             case PROP_MODE:
00468                 property >> layer.mode;
00469                 break;
00470 
00471             case PROP_TATTOO:
00472                 property >> layer.tattoo;
00473                 break;
00474 
00475             default:
00476                 kDebug(399) << "XCF: unimplemented layer property " << type
00477                         << ", size " << bytes.size() << endl;
00478         }
00479     }
00480 }
00481 
00482 
00488 bool XCFImageFormat::composeTiles(XCFImage& xcf_image)
00489 {
00490     Layer& layer(xcf_image.layer);
00491 
00492     layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
00493     layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
00494 
00495     layer.image_tiles.resize(layer.nrows);
00496 
00497     if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE)
00498         layer.alpha_tiles.resize(layer.nrows);
00499 
00500     if (layer.mask_offset != 0)
00501         layer.mask_tiles.resize(layer.nrows);
00502 
00503     for (uint j = 0; j < layer.nrows; j++) {
00504         layer.image_tiles[j].resize(layer.ncols);
00505 
00506         if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE)
00507             layer.alpha_tiles[j].resize(layer.ncols);
00508 
00509         if (layer.mask_offset != 0)
00510             layer.mask_tiles[j].resize(layer.ncols);
00511     }
00512 
00513     for (uint j = 0; j < layer.nrows; j++) {
00514         for (uint i = 0; i < layer.ncols; i++) {
00515 
00516             uint tile_width = (i + 1) * TILE_WIDTH <= layer.width
00517                     ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
00518 
00519             uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height
00520                     ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
00521 
00522             // Try to create the most appropriate QImage (each GIMP layer
00523             // type is treated slightly differently)
00524 
00525             switch (layer.type) {
00526                 case RGB_GIMAGE:
00527                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_RGB32);
00528                     layer.image_tiles[j][i].setNumColors(0);
00529                     if( layer.image_tiles[j][i].isNull())
00530                         return false;
00531                     break;
00532 
00533                 case RGBA_GIMAGE:
00534                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_ARGB32);
00535                     layer.image_tiles[j][i].setNumColors(0);
00536                     if( layer.image_tiles[j][i].isNull())
00537                         return false;
00538                     break;
00539 
00540                 case GRAY_GIMAGE:
00541                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00542                     layer.image_tiles[j][i].setNumColors(256);
00543                     if( layer.image_tiles[j][i].isNull())
00544                         return false;
00545                     setGrayPalette(layer.image_tiles[j][i]);
00546                     break;
00547 
00548                 case GRAYA_GIMAGE:
00549                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00550                     layer.image_tiles[j][i].setNumColors(256);
00551                     if( layer.image_tiles[j][i].isNull())
00552                         return false;
00553                     setGrayPalette(layer.image_tiles[j][i]);
00554 
00555                     layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00556                     layer.alpha_tiles[j][i].setNumColors(256);
00557                     if( layer.alpha_tiles[j][i].isNull())
00558                         return false;
00559                     setGrayPalette(layer.alpha_tiles[j][i]);
00560                     break;
00561 
00562                 case INDEXED_GIMAGE:
00563                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00564                     layer.image_tiles[j][i].setNumColors(xcf_image.num_colors);
00565                     if( layer.image_tiles[j][i].isNull())
00566                         return false;
00567                     setPalette(xcf_image, layer.image_tiles[j][i]);
00568                     break;
00569 
00570                 case INDEXEDA_GIMAGE:
00571                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00572                     layer.image_tiles[j][i].setNumColors(xcf_image.num_colors);
00573                     if( layer.image_tiles[j][i].isNull())
00574                         return false;
00575                     setPalette(xcf_image, layer.image_tiles[j][i]);
00576 
00577                     layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00578                     layer.alpha_tiles[j][i].setNumColors(256);
00579                     if( layer.alpha_tiles[j][i].isNull())
00580                         return false;
00581                     setGrayPalette(layer.alpha_tiles[j][i]);
00582             }
00583 
00584             if (layer.mask_offset != 0) {
00585                 layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00586                 layer.mask_tiles[j][i].setNumColors(256);
00587                 if( layer.mask_tiles[j][i].isNull())
00588                     return false;
00589                 setGrayPalette(layer.mask_tiles[j][i]);
00590             }
00591         }
00592     }
00593     return true;
00594 }
00595 
00596 
00603 void XCFImageFormat::setGrayPalette(QImage& image)
00604 {
00605     for (int i = 0; i < 256; i++)
00606         image.setColor(i, qRgb(i, i, i));
00607 }
00608 
00609 
00615 void XCFImageFormat::setPalette(XCFImage& xcf_image, QImage& image)
00616 {
00617     for (int i = 0; i < xcf_image.num_colors; i++)
00618         image.setColor(i, xcf_image.palette[i]);
00619 }
00620 
00621 
00629 void XCFImageFormat::assignImageBytes(Layer& layer, uint i, uint j)
00630 {
00631     uchar* tile = layer.tile;
00632 
00633     switch (layer.type) {
00634         case RGB_GIMAGE:
00635             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
00636                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
00637                     layer.image_tiles[j][i].setPixel(k, l,
00638                             qRgb(tile[0], tile[1], tile[2]));
00639                     tile += sizeof(QRgb);
00640                 }
00641             }
00642             break;
00643 
00644         case RGBA_GIMAGE:
00645             for ( int l = 0; l < layer.image_tiles[j][i].height(); l++ ) {
00646                 for ( int k = 0; k < layer.image_tiles[j][i].width(); k++ ) {
00647                     layer.image_tiles[j][i].setPixel(k, l,
00648                             qRgba(tile[0], tile[1], tile[2], tile[3]));
00649                     tile += sizeof(QRgb);
00650                 }
00651             }
00652             break;
00653 
00654         case GRAY_GIMAGE:
00655         case INDEXED_GIMAGE:
00656             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
00657                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
00658                     layer.image_tiles[j][i].setPixel(k, l, tile[0]);
00659                     tile += sizeof(QRgb);
00660                 }
00661             }
00662             break;
00663 
00664         case GRAYA_GIMAGE:
00665         case INDEXEDA_GIMAGE:
00666             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
00667                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
00668 
00669                 // The "if" here should not be necessary, but apparently there
00670                 // are some cases where the image can contain larger indices
00671                 // than there are colors in the palette. (A bug in The GIMP?)
00672 
00673                     if (tile[0] < layer.image_tiles[j][i].numColors())
00674                         layer.image_tiles[j][i].setPixel(k, l, tile[0]);
00675 
00676                     layer.alpha_tiles[j][i].setPixel(k, l, tile[1]);
00677                     tile += sizeof(QRgb);
00678                 }
00679             }
00680             break;
00681     }
00682 }
00683 
00684 
00693 bool XCFImageFormat::loadHierarchy(QDataStream& xcf_io, Layer& layer)
00694 {
00695     qint32 width;
00696     qint32 height;
00697     qint32 bpp;
00698     quint32 offset;
00699 
00700     xcf_io >> width >> height >> bpp >> offset;
00701 
00702     // GIMP stores images in a "mipmap"-like format (multiple levels of
00703     // increasingly lower resolution). Only the top level is used here,
00704     // however.
00705 
00706     quint32 junk;
00707     do {
00708         xcf_io >> junk;
00709 
00710         if (xcf_io.device()->atEnd()) {
00711             kDebug(399) << "XCF: read failure on layer " << layer.name << " level offsets";
00712             return false;
00713         }
00714     } while (junk != 0);
00715 
00716     qint64 saved_pos = xcf_io.device()->pos();
00717 
00718     xcf_io.device()->seek(offset);
00719     if (!loadLevel(xcf_io, layer, bpp))
00720         return false;
00721 
00722     xcf_io.device()->seek(saved_pos);
00723     return true;
00724 }
00725 
00726 
00735 bool XCFImageFormat::loadLevel(QDataStream& xcf_io, Layer& layer, qint32 bpp)
00736 {
00737     qint32 width;
00738     qint32 height;
00739     quint32 offset;
00740 
00741     xcf_io >> width >> height >> offset;
00742 
00743     if (offset == 0)
00744         return true;
00745 
00746     for (uint j = 0; j < layer.nrows; j++) {
00747         for (uint i = 0; i < layer.ncols; i++) {
00748 
00749             if (offset == 0) {
00750                 kDebug(399) << "XCF: incorrect number of tiles in layer " << layer.name;
00751                 return false;
00752             }
00753 
00754             qint64 saved_pos = xcf_io.device()->pos();
00755             quint32 offset2;
00756             xcf_io >> offset2;
00757 
00758             // Evidently, RLE can occasionally expand a tile instead of compressing it!
00759 
00760             if (offset2 == 0)
00761                 offset2 = offset + (uint)(TILE_WIDTH * TILE_HEIGHT * 4 * 1.5);
00762 
00763             xcf_io.device()->seek(offset);
00764             int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
00765 
00766             if (!loadTileRLE(xcf_io, layer.tile, size, offset2 - offset, bpp))
00767                 return false;
00768 
00769             // The bytes in the layer tile are juggled differently depending on
00770             // the target QImage. The caller has set layer.assignBytes to the
00771             // appropriate routine.
00772 
00773             layer.assignBytes(layer, i, j);
00774 
00775             xcf_io.device()->seek(saved_pos);
00776             xcf_io >> offset;
00777         }
00778     }
00779 
00780     return true;
00781 }
00782 
00783 
00790 bool XCFImageFormat::loadMask(QDataStream& xcf_io, Layer& layer)
00791 {
00792     qint32 width;
00793     qint32 height;
00794     char* name;
00795 
00796     xcf_io >> width >> height >> name;
00797 
00798     delete name;
00799 
00800     if (!loadChannelProperties(xcf_io, layer))
00801         return false;
00802 
00803     quint32 hierarchy_offset;
00804     xcf_io >> hierarchy_offset;
00805 
00806     xcf_io.device()->seek(hierarchy_offset);
00807     layer.assignBytes = assignMaskBytes;
00808 
00809     if (!loadHierarchy(xcf_io, layer))
00810         return false;
00811 
00812     return true;
00813 }
00814 
00815 
00839 bool XCFImageFormat::loadTileRLE(QDataStream& xcf_io, uchar* tile, int image_size,
00840         int data_length, qint32 bpp)
00841 {
00842     uchar* data;
00843 
00844     uchar* xcfdata;
00845     uchar* xcfodata;
00846     uchar* xcfdatalimit;
00847 
00848     xcfdata = xcfodata = new uchar[data_length];
00849 
00850     xcf_io.readRawData((char*)xcfdata, data_length);
00851 
00852     if (!xcf_io.device()->isOpen()) {
00853         delete[] xcfodata;
00854         kDebug(399) << "XCF: read failure on tile";
00855         return false;
00856     }
00857 
00858     xcfdatalimit = &xcfodata[data_length - 1];
00859 
00860     for (int i = 0; i < bpp; ++i) {
00861 
00862         data = tile + i;
00863 
00864         int count = 0;
00865         int size = image_size;
00866 
00867         while (size > 0) {
00868             if (xcfdata > xcfdatalimit)
00869                 goto bogus_rle;
00870 
00871             uchar val = *xcfdata++;
00872             uint length = val;
00873 
00874             if (length >= 128) {
00875                 length = 255 - (length - 1);
00876                 if (length == 128) {
00877                     if (xcfdata >= xcfdatalimit)
00878                         goto bogus_rle;
00879 
00880                     length = (*xcfdata << 8) + xcfdata[1];
00881 
00882                     xcfdata += 2;
00883                 }
00884 
00885                 count += length;
00886                 size -= length;
00887 
00888                 if (size < 0)
00889                     goto bogus_rle;
00890 
00891                 if (&xcfdata[length - 1] > xcfdatalimit)
00892                     goto bogus_rle;
00893 
00894                 while (length-- > 0) {
00895                     *data = *xcfdata++;
00896                     data += sizeof(QRgb);
00897                 }
00898             } else {
00899                 length += 1;
00900                 if (length == 128) {
00901                     if (xcfdata >= xcfdatalimit)
00902                         goto bogus_rle;
00903 
00904                     length = (*xcfdata << 8) + xcfdata[1];
00905                     xcfdata += 2;
00906                 }
00907 
00908                 count += length;
00909                 size -= length;
00910 
00911                 if (size < 0)
00912                     goto bogus_rle;
00913 
00914                 if (xcfdata > xcfdatalimit)
00915                     goto bogus_rle;
00916 
00917                 val = *xcfdata++;
00918 
00919                 while (length-- > 0) {
00920                     *data = val;
00921                     data += sizeof(QRgb);
00922                 }
00923             }
00924         }
00925     }
00926 
00927     delete[] xcfodata;
00928     return true;
00929 
00930 bogus_rle:
00931 
00932     kDebug(399) << "The run length encoding could not be decoded properly";
00933     delete[] xcfodata;
00934     return false;
00935 }
00936 
00937 
00945 bool XCFImageFormat::loadChannelProperties(QDataStream& xcf_io, Layer& layer)
00946 {
00947     while (true) {
00948         PropType type;
00949         QByteArray bytes;
00950 
00951         if (!loadProperty(xcf_io, type, bytes)) {
00952             kDebug(399) << "XCF: error loading channel properties";
00953             return false;
00954         }
00955 
00956         QDataStream property(bytes);
00957 
00958         switch (type) {
00959             case PROP_END:
00960                 return true;
00961 
00962             case PROP_OPACITY:
00963                 property >> layer.mask_channel.opacity;
00964                 break;
00965 
00966             case PROP_VISIBLE:
00967                 property >> layer.mask_channel.visible;
00968                 break;
00969 
00970             case PROP_SHOW_MASKED:
00971                 property >> layer.mask_channel.show_masked;
00972                 break;
00973 
00974             case PROP_COLOR:
00975                 property >> layer.mask_channel.red >> layer.mask_channel.green
00976                         >> layer.mask_channel.blue;
00977                 break;
00978 
00979             case PROP_TATTOO:
00980                 property >> layer.mask_channel.tattoo;
00981                 break;
00982 
00983             default:
00984                 kDebug(399) << "XCF: unimplemented channel property " << type
00985                         << ", size " << bytes.size() << endl;
00986         }
00987     }
00988 }
00989 
00990 
00997 void XCFImageFormat::assignMaskBytes(Layer& layer, uint i, uint j)
00998 {
00999     uchar* tile = layer.tile;
01000 
01001     for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01002         for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01003             layer.mask_tiles[j][i].setPixel(k, l, tile[0]);
01004             tile += sizeof(QRgb);
01005         }
01006     }
01007 }
01008 
01009 
01038 bool XCFImageFormat::initializeImage(XCFImage& xcf_image)
01039 {
01040     // (Aliases to make the code look a little better.)
01041     Layer& layer(xcf_image.layer);
01042     QImage& image(xcf_image.image);
01043 
01044     switch (layer.type) {
01045         case RGB_GIMAGE:
01046             if (layer.opacity == OPAQUE_OPACITY) {
01047                 image = QImage( xcf_image.width, xcf_image.height, QImage::Format_RGB32);
01048                 if( image.isNull())
01049                     return false;
01050                 image.fill(qRgb(255, 255, 255));
01051                 break;
01052             } // else, fall through to 32-bit representation
01053 
01054         case RGBA_GIMAGE:
01055             image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01056             if( image.isNull())
01057                 return false;
01058             image.fill(qRgba(255, 255, 255, 0));
01059             break;
01060 
01061         case GRAY_GIMAGE:
01062             if (layer.opacity == OPAQUE_OPACITY) {
01063                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01064                 image.setNumColors(256);
01065                 if( image.isNull())
01066                     return false;
01067                 setGrayPalette(image);
01068                 image.fill(255);
01069                 break;
01070             } // else, fall through to 32-bit representation
01071 
01072         case GRAYA_GIMAGE:
01073             image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01074             if( image.isNull())
01075                 return false;
01076             image.fill(qRgba(255, 255, 255, 0));
01077             break;
01078 
01079         case INDEXED_GIMAGE:
01080             // As noted in the table above, there are quite a few combinations
01081             // which are possible with indexed images, depending on the
01082             // presence of transparency (note: not translucency, which is not
01083             // supported by The GIMP for indexed images) and the number of
01084             // individual colors.
01085 
01086             // Note: Qt treats a bitmap with a Black and White color palette
01087             // as a mask, so only the "on" bits are drawn, regardless of the
01088             // order color table entries. Otherwise (i.e., at least one of the
01089             // color table entries is not black or white), it obeys the one-
01090             // or two-color palette. Have to ask about this...
01091 
01092             if (xcf_image.num_colors <= 2) {
01093                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
01094                 image.setNumColors(xcf_image.num_colors);
01095                 if( image.isNull())
01096                     return false;
01097                 image.fill(0);
01098                 setPalette(xcf_image, image);
01099             } else if (xcf_image.num_colors <= 256) {
01100                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01101                 image.setNumColors(xcf_image.num_colors);
01102                 if( image.isNull())
01103                     return false;
01104                 image.fill(0);
01105                 setPalette(xcf_image, image);
01106             }
01107             break;
01108 
01109         case INDEXEDA_GIMAGE:
01110             if (xcf_image.num_colors == 1) {
01111                 // Plenty(!) of room to add a transparent color
01112                 xcf_image.num_colors++;
01113                 xcf_image.palette.resize(xcf_image.num_colors);
01114                 xcf_image.palette[1] = xcf_image.palette[0];
01115                 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
01116 
01117                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
01118                 image.setNumColors(xcf_image.num_colors);
01119                 if( image.isNull())
01120                     return false;
01121                 image.fill(0);
01122                 setPalette(xcf_image, image);
01123             } else if (xcf_image.num_colors < 256) {
01124                 // Plenty of room to add a transparent color
01125                 xcf_image.num_colors++;
01126                 xcf_image.palette.resize(xcf_image.num_colors);
01127                 for (int c = xcf_image.num_colors - 1; c >= 1; c--)
01128                     xcf_image.palette[c] = xcf_image.palette[c - 1];
01129 
01130                 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
01131                 image = QImage( xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01132                 image.setNumColors(xcf_image.num_colors);
01133                 if( image.isNull())
01134                     return false;
01135                 image.fill(0);
01136                 setPalette(xcf_image, image);
01137             } else {
01138                 // No room for a transparent color, so this has to be promoted to
01139                 // true color. (There is no equivalent PNG representation output
01140                 // from The GIMP as of v1.2.)
01141                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01142                 if( image.isNull())
01143                     return false;
01144                 image.fill(qRgba(255, 255, 255, 0));
01145             }
01146             break;
01147     }
01148 
01149     image.setDotsPerMeterX((int)(xcf_image.x_resolution * INCHESPERMETER));
01150     image.setDotsPerMeterY((int)(xcf_image.y_resolution * INCHESPERMETER));
01151     return true;
01152 }
01153 
01154 
01160 void XCFImageFormat::copyLayerToImage(XCFImage& xcf_image)
01161 {
01162     Layer& layer(xcf_image.layer);
01163     QImage& image(xcf_image.image);
01164     PixelCopyOperation copy = 0;
01165 
01166     switch (layer.type) {
01167         case RGB_GIMAGE:
01168         case RGBA_GIMAGE:
01169             copy = copyRGBToRGB;
01170             break;
01171         case GRAY_GIMAGE:
01172             if (layer.opacity == OPAQUE_OPACITY)
01173                 copy = copyGrayToGray;
01174             else
01175                 copy = copyGrayToRGB;
01176             break;
01177         case GRAYA_GIMAGE:
01178             copy = copyGrayAToRGB;
01179             break;
01180         case INDEXED_GIMAGE:
01181             copy = copyIndexedToIndexed;
01182             break;
01183         case INDEXEDA_GIMAGE:
01184             if (xcf_image.image.depth() <= 8)
01185                 copy = copyIndexedAToIndexed;
01186             else
01187                 copy = copyIndexedAToRGB;
01188     }
01189 
01190     if (!copy) {
01191         return;
01192     }
01193 
01194     // For each tile...
01195 
01196     for (uint j = 0; j < layer.nrows; j++) {
01197         uint y = j * TILE_HEIGHT;
01198 
01199         for (uint i = 0; i < layer.ncols; i++) {
01200             uint x = i * TILE_WIDTH;
01201 
01202             // This seems the best place to apply the dissolve because it
01203             // depends on the global position of each tile's
01204             // pixels. Apparently it's the only mode which can apply to a
01205             // single layer.
01206 
01207             if (layer.mode == DISSOLVE_MODE) {
01208                 if (layer.type == RGBA_GIMAGE)
01209                     dissolveRGBPixels(layer.image_tiles[j][i], x, y);
01210 
01211                 else if (layer.type == GRAYA_GIMAGE)
01212                     dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
01213             }
01214 
01215             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01216                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01217 
01218                     int m = x + k + layer.x_offset;
01219                     int n = y + l + layer.y_offset;
01220 
01221                     if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
01222                         continue;
01223 
01224                     (*copy)(layer, i, j, k, l, image, m, n);
01225                 }
01226             }
01227         }
01228     }
01229 }
01230 
01231 
01245 void XCFImageFormat::copyRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
01246         QImage& image, int m, int n)
01247 {
01248     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01249     uchar src_a = layer.opacity;
01250 
01251     if (layer.type == RGBA_GIMAGE)
01252         src_a = INT_MULT(src_a, qAlpha(src));
01253 
01254     // Apply the mask (if any)
01255 
01256     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01257             layer.mask_tiles[j].size() > (int)i)
01258         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01259 
01260     image.setPixel(m, n, qRgba(src, src_a));
01261 }
01262 
01263 
01275 void XCFImageFormat::copyGrayToGray(Layer& layer, uint i, uint j, int k, int l,
01276         QImage& image, int m, int n)
01277 {
01278     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01279     image.setPixel(m, n, src);
01280 }
01281 
01282 
01296 void XCFImageFormat::copyGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
01297         QImage& image, int m, int n)
01298 {
01299     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01300     uchar src_a = layer.opacity;
01301     image.setPixel(m, n, qRgba(src, src_a));
01302 }
01303 
01304 
01318 void XCFImageFormat::copyGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
01319                       QImage& image, int m, int n)
01320 {
01321     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01322     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01323     src_a = INT_MULT(src_a, layer.opacity);
01324 
01325     // Apply the mask (if any)
01326 
01327     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01328             layer.mask_tiles[j].size() > (int)i)
01329         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01330 
01331     image.setPixel(m, n, qRgba(src, src_a));
01332 }
01333 
01334 
01346 void XCFImageFormat::copyIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
01347         QImage& image, int m, int n)
01348 {
01349     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01350     image.setPixel(m, n, src);
01351 }
01352 
01353 
01365 void XCFImageFormat::copyIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
01366         QImage& image, int m, int n)
01367 {
01368     uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
01369     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01370     src_a = INT_MULT(src_a, layer.opacity);
01371 
01372     if (layer.apply_mask == 1 &&
01373             layer.mask_tiles.size() > (int)j &&
01374             layer.mask_tiles[j].size() > (int)i)
01375         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01376 
01377     if (src_a > 127)
01378         src++;
01379     else
01380         src = 0;
01381 
01382 image.setPixel(m, n, src);
01383 }
01384 
01385 
01399 void XCFImageFormat::copyIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
01400         QImage& image, int m, int n)
01401 {
01402     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01403     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01404     src_a = INT_MULT(src_a, layer.opacity);
01405 
01406     // Apply the mask (if any)
01407     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01408             layer.mask_tiles[j].size() > (int)i)
01409         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01410 
01411     // This is what appears in the GIMP window
01412     if (src_a <= 127)
01413         src_a = 0;
01414     else
01415         src_a = OPAQUE_OPACITY;
01416 
01417     image.setPixel(m, n, qRgba(src, src_a));
01418 }
01419 
01420 
01425 void XCFImageFormat::mergeLayerIntoImage(XCFImage& xcf_image)
01426 {
01427     Layer& layer(xcf_image.layer);
01428     QImage& image(xcf_image.image);
01429 
01430     PixelMergeOperation merge = 0;
01431 
01432     if (!layer.opacity) return; // don't bother doing anything
01433 
01434     switch (layer.type) {
01435         case RGB_GIMAGE:
01436         case RGBA_GIMAGE:
01437             merge = mergeRGBToRGB;
01438             break;
01439         case GRAY_GIMAGE:
01440             if (layer.opacity == OPAQUE_OPACITY)
01441                 merge = mergeGrayToGray;
01442             else
01443                 merge = mergeGrayToRGB;
01444             break;
01445         case GRAYA_GIMAGE:
01446             if (xcf_image.image.depth() <= 8)
01447                 merge = mergeGrayAToGray;
01448             else
01449                 merge = mergeGrayAToRGB;
01450             break;
01451         case INDEXED_GIMAGE:
01452             merge = mergeIndexedToIndexed;
01453             break;
01454         case INDEXEDA_GIMAGE:
01455             if (xcf_image.image.depth() <= 8)
01456                 merge = mergeIndexedAToIndexed;
01457             else
01458                 merge = mergeIndexedAToRGB;
01459     }
01460 
01461     if (!merge) {
01462         return;
01463     }
01464 
01465     for (uint j = 0; j < layer.nrows; j++) {
01466         uint y = j * TILE_HEIGHT;
01467 
01468         for (uint i = 0; i < layer.ncols; i++) {
01469             uint x = i * TILE_WIDTH;
01470 
01471             // This seems the best place to apply the dissolve because it
01472             // depends on the global position of each tile's
01473             // pixels. Apparently it's the only mode which can apply to a
01474             // single layer.
01475 
01476             if (layer.mode == DISSOLVE_MODE) {
01477                 if (layer.type == RGBA_GIMAGE)
01478                     dissolveRGBPixels(layer.image_tiles[j][i], x, y);
01479 
01480                 else if (layer.type == GRAYA_GIMAGE)
01481                     dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
01482             }
01483 
01484             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01485                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01486 
01487                     int m = x + k + layer.x_offset;
01488                     int n = y + l + layer.y_offset;
01489 
01490                     if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
01491                         continue;
01492 
01493                     (*merge)(layer, i, j, k, l, image, m, n);
01494                 }
01495             }
01496         }
01497     }
01498 }
01499 
01500 
01514 void XCFImageFormat::mergeRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
01515         QImage& image, int m, int n)
01516 {
01517     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01518     QRgb dst = image.pixel(m, n);
01519 
01520     uchar src_r = qRed(src);
01521     uchar src_g = qGreen(src);
01522     uchar src_b = qBlue(src);
01523     uchar src_a = qAlpha(src);
01524 
01525     uchar dst_r = qRed(dst);
01526     uchar dst_g = qGreen(dst);
01527     uchar dst_b = qBlue(dst);
01528     uchar dst_a = qAlpha(dst);
01529 
01530     if (!src_a) return; // nothing to merge
01531 
01532     switch (layer.mode) {
01533         case MULTIPLY_MODE: {
01534             src_r = INT_MULT(src_r, dst_r);
01535             src_g = INT_MULT(src_g, dst_g);
01536             src_b = INT_MULT(src_b, dst_b);
01537             src_a = qMin(src_a, dst_a);
01538             }
01539             break;
01540         case DIVIDE_MODE: {
01541             src_r = qMin((dst_r * 256) / (1 + src_r), 255);
01542             src_g = qMin((dst_g * 256) / (1 + src_g), 255);
01543             src_b = qMin((dst_b * 256) / (1 + src_b), 255);
01544             src_a = qMin(src_a, dst_a);
01545             }
01546             break;
01547         case SCREEN_MODE: {
01548             src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
01549             src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
01550             src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
01551             src_a = qMin(src_a, dst_a);
01552             }
01553             break;
01554         case OVERLAY_MODE: {
01555             src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
01556             src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
01557             src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
01558             src_a = qMin(src_a, dst_a);
01559             }
01560             break;
01561         case DIFFERENCE_MODE: {
01562             src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
01563             src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
01564             src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
01565             src_a = qMin(src_a, dst_a);
01566             }
01567             break;
01568         case ADDITION_MODE: {
01569               src_r = add_lut(dst_r,src_r);
01570               src_g = add_lut(dst_g,src_g);
01571               src_b = add_lut(dst_b,src_b);
01572               src_a = qMin(src_a, dst_a);
01573             }
01574             break;
01575         case SUBTRACT_MODE: {
01576             src_r = dst_r > src_r ? dst_r - src_r : 0;
01577             src_g = dst_g > src_g ? dst_g - src_g : 0;
01578             src_b = dst_b > src_b ? dst_b - src_b : 0;
01579             src_a = qMin(src_a, dst_a);
01580             }
01581             break;
01582         case DARKEN_ONLY_MODE: {
01583             src_r = dst_r < src_r ? dst_r : src_r;
01584             src_g = dst_g < src_g ? dst_g : src_g;
01585             src_b = dst_b < src_b ? dst_b : src_b;
01586             src_a = qMin( src_a, dst_a );
01587             }
01588             break;
01589         case LIGHTEN_ONLY_MODE: {
01590             src_r = dst_r < src_r ? src_r : dst_r;
01591             src_g = dst_g < src_g ? src_g : dst_g;
01592             src_b = dst_b < src_b ? src_b : dst_b;
01593             src_a = qMin(src_a, dst_a);
01594             }
01595             break;
01596         case HUE_MODE: {
01597             uchar new_r = dst_r;
01598             uchar new_g = dst_g;
01599             uchar new_b = dst_b;
01600 
01601             RGBTOHSV(src_r, src_g, src_b);
01602             RGBTOHSV(new_r, new_g, new_b);
01603 
01604             new_r = src_r;
01605 
01606             HSVTORGB(new_r, new_g, new_b);
01607 
01608             src_r = new_r;
01609             src_g = new_g;
01610             src_b = new_b;
01611             src_a = qMin( src_a, dst_a );
01612             }
01613             break;
01614         case SATURATION_MODE: {
01615             uchar new_r = dst_r;
01616             uchar new_g = dst_g;
01617             uchar new_b = dst_b;
01618 
01619             RGBTOHSV(src_r, src_g, src_b);
01620             RGBTOHSV(new_r, new_g, new_b);
01621 
01622             new_g = src_g;
01623 
01624             HSVTORGB(new_r, new_g, new_b);
01625 
01626             src_r = new_r;
01627             src_g = new_g;
01628             src_b = new_b;
01629             src_a = qMin(src_a, dst_a);
01630             }
01631             break;
01632         case VALUE_MODE: {
01633             uchar new_r = dst_r;
01634             uchar new_g = dst_g;
01635             uchar new_b = dst_b;
01636 
01637             RGBTOHSV(src_r, src_g, src_b);
01638             RGBTOHSV(new_r, new_g, new_b);
01639 
01640             new_b = src_b;
01641 
01642             HSVTORGB(new_r, new_g, new_b);
01643 
01644             src_r = new_r;
01645             src_g = new_g;
01646             src_b = new_b;
01647             src_a = qMin(src_a, dst_a);
01648             }
01649             break;
01650         case COLOR_MODE: {
01651             uchar new_r = dst_r;
01652             uchar new_g = dst_g;
01653             uchar new_b = dst_b;
01654 
01655             RGBTOHLS(src_r, src_g, src_b);
01656             RGBTOHLS(new_r, new_g, new_b);
01657 
01658             new_r = src_r;
01659             new_b = src_b;
01660 
01661             HLSTORGB(new_r, new_g, new_b);
01662 
01663             src_r = new_r;
01664             src_g = new_g;
01665             src_b = new_b;
01666             src_a = qMin(src_a, dst_a);
01667             }
01668             break;
01669         case DODGE_MODE: {
01670             uint tmp;
01671 
01672             tmp = dst_r << 8;
01673             tmp /= 256 - src_r;
01674             src_r = (uchar) qMin(tmp, 255u);
01675 
01676             tmp = dst_g << 8;
01677             tmp /= 256 - src_g;
01678             src_g = (uchar) qMin(tmp, 255u);
01679 
01680             tmp = dst_b << 8;
01681             tmp /= 256 - src_b;
01682             src_b = (uchar) qMin(tmp, 255u);
01683 
01684             src_a = qMin(src_a, dst_a);
01685             }
01686             break;
01687         case BURN_MODE: {
01688             uint tmp;
01689 
01690             tmp = (255 - dst_r) << 8;
01691             tmp /= src_r + 1;
01692             src_r = (uchar) qMin(tmp, 255u);
01693             src_r = 255 - src_r;
01694 
01695             tmp = (255 - dst_g) << 8;
01696             tmp /= src_g + 1;
01697             src_g = (uchar) qMin(tmp, 255u);
01698             src_g = 255 - src_g;
01699 
01700             tmp = (255 - dst_b) << 8;
01701             tmp /= src_b + 1;
01702             src_b = (uchar) qMin(tmp, 255u);
01703             src_b = 255 - src_b;
01704 
01705             src_a = qMin(src_a, dst_a);
01706             }
01707             break;
01708         case HARDLIGHT_MODE: {
01709             uint tmp;
01710             if (src_r > 128) {
01711                 tmp = ((int)255-dst_r) * ((int) 255 - ((src_r-128) << 1));
01712                 src_r = (uchar) qMin(255 - (tmp >> 8), 255u);
01713             } else {
01714                 tmp = (int) dst_r * ((int) src_r << 1);
01715                 src_r = (uchar) qMin(tmp >> 8, 255u);
01716             }
01717 
01718             if (src_g > 128) {
01719                 tmp = ((int)255-dst_g) * ((int) 255 - ((src_g-128) << 1));
01720                 src_g = (uchar) qMin(255 - (tmp >> 8), 255u);
01721             } else {
01722                 tmp = (int) dst_g * ((int) src_g << 1);
01723                 src_g = (uchar) qMin(tmp >> 8, 255u);
01724             }
01725 
01726             if (src_b > 128) {
01727                 tmp = ((int)255-dst_b) * ((int) 255 - ((src_b-128) << 1));
01728                 src_b = (uchar) qMin(255 - (tmp >> 8), 255u);
01729             } else {
01730                 tmp = (int) dst_b * ((int) src_b << 1);
01731                 src_b = (uchar) qMin(tmp >> 8, 255u);
01732             }
01733             src_a = qMin(src_a, dst_a);
01734             }
01735             break;
01736         case SOFTLIGHT_MODE: {
01737             uint tmpS, tmpM;
01738 
01739             tmpM = INT_MULT(dst_r, src_r);
01740             tmpS = 255 - INT_MULT((255 - dst_r), (255-src_r));
01741             src_r = INT_MULT((255 - dst_r), tmpM)
01742                 + INT_MULT(dst_r, tmpS);
01743 
01744             tmpM = INT_MULT(dst_g, src_g);
01745             tmpS = 255 - INT_MULT((255 - dst_g), (255-src_g));
01746             src_g = INT_MULT((255 - dst_g), tmpM)
01747                 + INT_MULT(dst_g, tmpS);
01748 
01749             tmpM = INT_MULT(dst_b, src_b);
01750             tmpS = 255 - INT_MULT((255 - dst_b), (255-src_b));
01751             src_b = INT_MULT((255 - dst_b), tmpM)
01752                 + INT_MULT(dst_b, tmpS);
01753 
01754             src_a = qMin(src_a, dst_a);
01755             }
01756             break;
01757         case GRAIN_EXTRACT_MODE: {
01758             int tmp;
01759             
01760             tmp = dst_r - src_r + 128;
01761             tmp = qMin(tmp, 255);
01762             tmp = qMax(tmp, 0);
01763             src_r = (uchar) tmp;
01764 
01765             tmp = dst_g - src_g + 128;
01766             tmp = qMin(tmp, 255);
01767             tmp = qMax(tmp, 0);
01768             src_g = (uchar) tmp;
01769 
01770             tmp = dst_b - src_b + 128;
01771             tmp = qMin(tmp, 255);
01772             tmp = qMax(tmp, 0);
01773             src_b = (uchar) tmp;
01774 
01775             src_a = qMin(src_a, dst_a);
01776             }
01777             break;
01778         case GRAIN_MERGE_MODE: {
01779             int tmp;
01780             
01781             tmp = dst_r + src_r - 128;
01782             tmp = qMin(tmp, 255);
01783             tmp = qMax(tmp, 0);
01784             src_r = (uchar) tmp;
01785 
01786             tmp = dst_g + src_g - 128;
01787             tmp = qMin(tmp, 255);
01788             tmp = qMax(tmp, 0);
01789             src_g = (uchar) tmp;
01790 
01791             tmp = dst_b + src_b - 128;
01792             tmp = qMin(tmp, 255);
01793             tmp = qMax(tmp, 0);
01794             src_b = (uchar) tmp;
01795 
01796             src_a = qMin(src_a, dst_a);
01797             }
01798             break;
01799     }
01800 
01801     src_a = INT_MULT(src_a, layer.opacity);
01802 
01803     // Apply the mask (if any)
01804 
01805     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01806             layer.mask_tiles[j].size() > (int)i)
01807         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01808 
01809     uchar new_r, new_g, new_b, new_a;
01810     new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
01811 
01812     float src_ratio = (float)src_a / new_a;
01813     float dst_ratio = 1.0 - src_ratio;
01814 
01815     new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
01816     new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
01817     new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
01818 
01819     if (!layer_modes[layer.mode].affect_alpha)
01820         new_a = dst_a;
01821 
01822     image.setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
01823 }
01824 
01825 
01837 void XCFImageFormat::mergeGrayToGray(Layer& layer, uint i, uint j, int k, int l,
01838         QImage& image, int m, int n)
01839 {
01840     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01841     image.setPixel(m, n, src);
01842 }
01843 
01844 
01856 void XCFImageFormat::mergeGrayAToGray(Layer& layer, uint i, uint j, int k, int l,
01857         QImage& image, int m, int n)
01858 {
01859     int src = qGray(layer.image_tiles[j][i].pixel(k, l));
01860     int dst = image.pixelIndex(m, n);
01861 
01862     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01863 
01864     if (!src_a) return; // nothing to merge
01865 
01866     switch (layer.mode) {
01867         case MULTIPLY_MODE: {
01868                 src = INT_MULT( src, dst );
01869             }
01870             break;
01871         case DIVIDE_MODE: {
01872                 src = qMin((dst * 256) / (1 + src), 255);
01873             }
01874             break;
01875         case SCREEN_MODE: {
01876                 src = 255 - INT_MULT(255 - dst, 255 - src);
01877             }
01878             break;
01879         case OVERLAY_MODE: {
01880                 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
01881             }
01882             break;
01883         case DIFFERENCE_MODE: {
01884                 src = dst > src ? dst - src : src - dst;
01885             }
01886             break;
01887         case ADDITION_MODE: {
01888                 src = add_lut(dst,src);
01889             }
01890             break;
01891         case SUBTRACT_MODE: {
01892                 src = dst > src ? dst - src : 0;
01893             }
01894             break;
01895         case DARKEN_ONLY_MODE: {
01896                 src = dst < src ? dst : src;
01897             }
01898             break;
01899         case LIGHTEN_ONLY_MODE: {
01900                 src = dst < src ? src : dst;
01901             }
01902             break;
01903         case DODGE_MODE: {
01904                 uint tmp = dst << 8;
01905                 tmp /= 256 - src;
01906                 src = (uchar) qMin(tmp, 255u);
01907             }
01908             break;
01909         case BURN_MODE: {
01910                 uint tmp = (255-dst) << 8;
01911                 tmp /= src + 1;
01912                 src = (uchar) qMin(tmp, 255u);
01913                 src = 255 - src;
01914             }
01915             break;
01916         case HARDLIGHT_MODE: {
01917                 uint tmp;
01918                 if (src > 128) {
01919                     tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1));
01920                     src = (uchar) qMin(255 - (tmp >> 8), 255u);
01921                 } else {
01922                     tmp = (int) dst * ((int) src << 1);
01923                     src = (uchar) qMin(tmp >> 8, 255u);
01924                 }
01925             }
01926             break;
01927         case SOFTLIGHT_MODE: {
01928                 uint tmpS, tmpM;
01929 
01930                 tmpM = INT_MULT(dst, src);
01931                 tmpS = 255 - INT_MULT((255-dst), (255-src));
01932                 src = INT_MULT((255 - dst), tmpM)
01933                     + INT_MULT(dst, tmpS);
01934 
01935             }
01936             break;
01937         case GRAIN_EXTRACT_MODE: {
01938                 int tmp;
01939                 
01940                 tmp = dst - src + 128;
01941                 tmp = qMin(tmp, 255);
01942                 tmp = qMax(tmp, 0);
01943 
01944                 src = (uchar) tmp;
01945             }
01946             break;
01947         case GRAIN_MERGE_MODE: {
01948                 int tmp;
01949                 
01950                 tmp = dst + src - 128;
01951                 tmp = qMin(tmp, 255);
01952                 tmp = qMax(tmp, 0);
01953 
01954                 src = (uchar) tmp;
01955             }
01956             break;
01957     }
01958 
01959     src_a = INT_MULT(src_a, layer.opacity);
01960 
01961     // Apply the mask (if any)
01962 
01963     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01964             layer.mask_tiles[j].size() > (int)i)
01965         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01966 
01967     uchar new_a = OPAQUE_OPACITY;
01968 
01969     float src_ratio = (float)src_a / new_a;
01970     float dst_ratio = 1.0 - src_ratio;
01971 
01972     uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
01973 
01974     image.setPixel(m, n, new_g);
01975 }
01976 
01977 
01991 void XCFImageFormat::mergeGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
01992         QImage& image, int m, int n)
01993 {
01994     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01995     uchar src_a = layer.opacity;
01996     image.setPixel(m, n, qRgba(src, src_a));
01997 }
01998 
01999 
02013 void XCFImageFormat::mergeGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
02014         QImage& image, int m, int n)
02015 {
02016     int src = qGray(layer.image_tiles[j][i].pixel(k, l));
02017     int dst = qGray(image.pixel(m, n));
02018 
02019     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02020     uchar dst_a = qAlpha(image.pixel(m, n));
02021 
02022     if (!src_a) return; // nothing to merge
02023 
02024     switch (layer.mode) {
02025         case MULTIPLY_MODE: {
02026                 src = INT_MULT(src, dst);
02027                 src_a = qMin(src_a, dst_a);
02028             }
02029             break;
02030         case DIVIDE_MODE: {
02031                 src = qMin((dst * 256) / (1 + src), 255);
02032                 src_a = qMin(src_a, dst_a);
02033             }
02034             break;
02035         case SCREEN_MODE: {
02036                 src = 255 - INT_MULT(255 - dst, 255 - src);
02037                 src_a = qMin(src_a, dst_a);
02038             }
02039             break;
02040         case OVERLAY_MODE: {
02041                 src = INT_MULT( dst, dst + INT_MULT(2 * src, 255 - dst));
02042                 src_a = qMin(src_a, dst_a);
02043             }
02044             break;
02045         case DIFFERENCE_MODE: {
02046                 src = dst > src ? dst - src : src - dst;
02047                 src_a = qMin(src_a, dst_a);
02048             }
02049             break;
02050         case ADDITION_MODE: {
02051                 src = add_lut(dst,src);
02052                 src_a = qMin(src_a, dst_a);
02053             }
02054             break;
02055         case SUBTRACT_MODE: {
02056                 src = dst > src ? dst - src : 0;
02057                 src_a = qMin(src_a, dst_a);
02058             }
02059             break;
02060         case DARKEN_ONLY_MODE: {
02061                 src = dst < src ? dst : src;
02062                 src_a = qMin(src_a, dst_a);
02063             }
02064             break;
02065         case LIGHTEN_ONLY_MODE: {
02066                 src = dst < src ? src : dst;
02067                 src_a = qMin(src_a, dst_a);
02068             }
02069             break;
02070         case DODGE_MODE: {
02071                 uint tmp = dst << 8;
02072                 tmp /= 256 - src;
02073                 src = (uchar) qMin(tmp, 255u);
02074                 src_a = qMin(src_a, dst_a);
02075             }
02076             break;
02077         case BURN_MODE: {
02078                 uint tmp = (255-dst) << 8;
02079                 tmp /= src + 1;
02080                 src = (uchar) qMin(tmp, 255u);
02081                 src = 255 - src;
02082                 src_a = qMin(src_a, dst_a);
02083             }
02084             break;
02085         case HARDLIGHT_MODE: {
02086                 uint tmp;
02087                 if (src > 128) {
02088                     tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1));
02089                     src = (uchar) qMin(255 - (tmp >> 8), 255u);
02090                 } else {
02091                     tmp = (int) dst * ((int) src << 1);
02092                     src = (uchar) qMin(tmp >> 8, 255u);
02093                 }
02094                 src_a = qMin(src_a, dst_a);
02095             }
02096             break;
02097         case SOFTLIGHT_MODE: {
02098                 uint tmpS, tmpM;
02099 
02100                 tmpM = INT_MULT(dst, src);
02101                 tmpS = 255 - INT_MULT((255 - dst), (255-src));
02102                 src = INT_MULT((255 - dst), tmpM)
02103                     + INT_MULT(dst, tmpS);
02104 
02105                 src_a = qMin(src_a, dst_a);
02106             }
02107             break;
02108         case GRAIN_EXTRACT_MODE: {
02109                 int tmp;
02110                 
02111                 tmp = dst - src + 128;
02112                 tmp = qMin(tmp, 255);
02113                 tmp = qMax(tmp, 0);
02114 
02115                 src = (uchar) tmp;
02116                 src_a = qMin(src_a, dst_a);
02117             }
02118             break;
02119         case GRAIN_MERGE_MODE: {
02120                 int tmp;
02121                 
02122                 tmp = dst + src - 128;
02123                 tmp = qMin(tmp, 255);
02124                 tmp = qMax(tmp, 0);
02125 
02126                 src = (uchar) tmp;
02127                 src_a = qMin(src_a, dst_a);
02128             }
02129             break;
02130     }
02131 
02132     src_a = INT_MULT(src_a, layer.opacity);
02133 
02134     // Apply the mask (if any)
02135     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02136             layer.mask_tiles[j].size() > (int)i)
02137         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02138 
02139     uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
02140 
02141     float src_ratio = (float)src_a / new_a;
02142     float dst_ratio = 1.0 - src_ratio;
02143 
02144     uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
02145 
02146     if (!layer_modes[layer.mode].affect_alpha)
02147         new_a = dst_a;
02148 
02149     image.setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
02150 }
02151 
02152 
02164 void XCFImageFormat::mergeIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
02165         QImage& image, int m, int n)
02166 {
02167     int src = layer.image_tiles[j][i].pixelIndex(k, l);
02168     image.setPixel(m, n, src);
02169 }
02170 
02171 
02183 void XCFImageFormat::mergeIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
02184         QImage& image, int m, int n)
02185 {
02186     uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
02187     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02188     src_a = INT_MULT( src_a, layer.opacity );
02189 
02190     if ( layer.apply_mask == 1 &&
02191             layer.mask_tiles.size() > (int)j &&
02192             layer.mask_tiles[j].size() > (int)i)
02193         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02194 
02195     if (src_a > 127) {
02196         src++;
02197         image.setPixel(m, n, src);
02198     }
02199 }
02200 
02201 
02215 void XCFImageFormat::mergeIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
02216         QImage& image, int m, int n)
02217 {
02218     QRgb src = layer.image_tiles[j][i].pixel(k, l);
02219     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02220     src_a = INT_MULT(src_a, layer.opacity);
02221 
02222     // Apply the mask (if any)
02223     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02224             layer.mask_tiles[j].size() > (int)i)
02225         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02226 
02227     // This is what appears in the GIMP window
02228     if (src_a <= 127)
02229         src_a = 0;
02230     else
02231         src_a = OPAQUE_OPACITY;
02232 
02233     image.setPixel(m, n, qRgba(src, src_a));
02234 }
02235 
02236 
02244 void XCFImageFormat::dissolveRGBPixels ( QImage& image, int x, int y )
02245 {
02246     // The apparently spurious rand() calls are to wind the random
02247     // numbers up to the same point for each tile.
02248 
02249     for (int l = 0; l < image.height(); l++) {
02250         srand(random_table[( l + y ) % RANDOM_TABLE_SIZE]);
02251 
02252         for (int k = 0; k < x; k++)
02253             rand();
02254 
02255         for (int k = 0; k < image.width(); k++) {
02256             int rand_val = rand() & 0xff;
02257             QRgb pixel = image.pixel(k, l);
02258 
02259             if (rand_val > qAlpha(pixel)) {
02260                 image.setPixel(k, l, qRgba(pixel, 0));
02261             }
02262         }
02263     }
02264 }
02265 
02266 
02276 void XCFImageFormat::dissolveAlphaPixels ( QImage& image, int x, int y )
02277 {
02278     // The apparently spurious rand() calls are to wind the random
02279     // numbers up to the same point for each tile.
02280 
02281     for (int l = 0; l < image.height(); l++) {
02282         srand( random_table[(l + y) % RANDOM_TABLE_SIZE]);
02283 
02284         for (int k = 0; k < x; k++)
02285             rand();
02286 
02287         for (int k = 0; k < image.width(); k++) {
02288             int rand_val = rand() & 0xff;
02289             uchar alpha = image.pixelIndex(k, l);
02290 
02291             if (rand_val > alpha) {
02292                 image.setPixel(k, l, 0);
02293             }
02294         }
02295     }
02296 }
02297 
02298 
02300 
02301 XCFHandler::XCFHandler()
02302 {
02303 }
02304 
02305 bool XCFHandler::canRead() const
02306 {
02307     if (canRead(device())) {
02308         setFormat("xcf");
02309         return true;
02310     }
02311     return false;
02312 }
02313 
02314 bool XCFHandler::read(QImage *image)
02315 {
02316     XCFImageFormat xcfif;
02317     return xcfif.readXCF(device(), image);
02318 }
02319 
02320 bool XCFHandler::write(const QImage &)
02321 {
02322     return false;
02323 }
02324 
02325 QByteArray XCFHandler::name() const
02326 {
02327     return "xcf";
02328 }
02329 
02330 bool XCFHandler::canRead(QIODevice *device)
02331 {
02332       if (!device) {
02333         qWarning("DDSHandler::canRead() called with no device");
02334         return false;
02335     }
02336 
02337     qint64 oldPos = device->pos();
02338 
02339     char head[8];
02340     qint64 readBytes = device->read(head, sizeof(head));
02341     if (readBytes != sizeof(head)) {
02342         if (device->isSequential()) {
02343             while (readBytes > 0)
02344                 device->ungetChar(head[readBytes-- - 1]);
02345         } else {
02346             device->seek(oldPos);
02347         }
02348         return false;
02349     }
02350 
02351     if (device->isSequential()) {
02352         while (readBytes > 0)
02353             device->ungetChar(head[readBytes-- - 1]);
02354     } else {
02355         device->seek(oldPos);
02356     }
02357 
02358     return qstrncmp(head, "gimp xcf", 8) == 0;
02359 }
02360 
02361 
02362 class XCFPlugin : public QImageIOPlugin
02363 {
02364 public:
02365     QStringList keys() const;
02366     Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
02367     QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
02368 };
02369 
02370 QStringList XCFPlugin::keys() const
02371 {
02372     return QStringList() << "xcf" << "XCF";
02373 }
02374 
02375 QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
02376 {
02377     if (format == "xcf" || format == "XCF")
02378         return Capabilities(CanRead);
02379     if (!format.isEmpty())
02380         return 0;
02381     if (!device->isOpen())
02382         return 0;
02383 
02384     Capabilities cap;
02385     if (device->isReadable() && XCFHandler::canRead(device))
02386         cap |= CanRead;
02387     return cap;
02388 }
02389 
02390 QImageIOHandler *XCFPlugin::create(QIODevice *device, const QByteArray &format) const
02391 {
02392     QImageIOHandler *handler = new XCFHandler;
02393     handler->setDevice(device);
02394     handler->setFormat(format);
02395     return handler;
02396 }
02397 
02398 Q_EXPORT_STATIC_PLUGIN(XCFPlugin)
02399 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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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