• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • 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 <QtGui/QPainter>
00027 #include <QtCore/QIODevice>
00028 #include <QtCore/QStack>
00029 #include <QtCore/QVector>
00030 
00031 #include <kdebug.h>
00032 
00033 
00034 int XCFImageFormat::random_table[RANDOM_TABLE_SIZE];
00035 bool XCFImageFormat::random_table_initialized;
00036 
00037 QVector<QRgb> XCFImageFormat::grayTable;
00038 
00039 
00040 const XCFImageFormat::LayerModes XCFImageFormat::layer_modes[] = {
00041     {true},     // NORMAL_MODE
00042     {true},     // DISSOLVE_MODE
00043     {true},     // BEHIND_MODE
00044     {false},    // MULTIPLY_MODE
00045     {false},    // SCREEN_MODE
00046     {false},    // OVERLAY_MODE
00047     {false},    // DIFFERENCE_MODE
00048     {false},    // ADDITION_MODE
00049     {false},    // SUBTRACT_MODE
00050     {false},    // DARKEN_ONLY_MODE
00051     {false},    // LIGHTEN_ONLY_MODE
00052     {false},    // HUE_MODE
00053     {false},    // SATURATION_MODE
00054     {false},    // COLOR_MODE
00055     {false},    // VALUE_MODE
00056     {false},    // DIVIDE_MODE
00057     {false},    // DODGE_MODE
00058     {false},    // BURN_MODE
00059     {false},    // HARDLIGHT_MODE
00060     {false},    // SOFTLIGHT_MODE
00061     {false},    // GRAIN_EXTRACT_MODE
00062     {false},    // GRAIN_MERGE_MODE
00063 };
00064 
00065 
00067 inline QRgb qRgba ( const QRgb& rgb, int a )
00068 {
00069     return ((a & 0xff) << 24 | (rgb & RGB_MASK));
00070 }
00071 
00072 
00076 XCFImageFormat::XCFImageFormat()
00077 {
00078 }
00079 
00083 void XCFImageFormat::initializeRandomTable()
00084 {
00085     // From GIMP "paint_funcs.c" v1.2
00086     srand(RANDOM_SEED);
00087 
00088     for (int i = 0; i < RANDOM_TABLE_SIZE; i++)
00089         random_table[i] = rand();
00090 
00091     for (int i = 0; i < RANDOM_TABLE_SIZE; i++) {
00092         int tmp;
00093         int swap = i + rand() % (RANDOM_TABLE_SIZE - i);
00094         tmp = random_table[i];
00095         random_table[i] = random_table[swap];
00096         random_table[swap] = tmp;
00097     }
00098 }
00099 
00100 inline
00101 int XCFImageFormat::add_lut( int a, int b ) {
00102     return qMin( a + b, 255 );
00103 }
00104 
00105 bool XCFImageFormat::readXCF(QIODevice *device, QImage *outImage)
00106 {
00107     XCFImage xcf_image;
00108     QDataStream xcf_io(device);
00109 
00110     char tag[14];;
00111 
00112     if (xcf_io.readRawData(tag, sizeof(tag)) != sizeof(tag)) {
00113             kDebug(399) << "XCF: read failure on header tag";
00114             return false;
00115     }
00116 
00117     xcf_io >> xcf_image.width >> xcf_image.height >> xcf_image.type;
00118 
00119 kDebug() << tag << " " << xcf_image.width << " " << xcf_image.height << " " <<  xcf_image.type;
00120     if (!loadImageProperties(xcf_io, xcf_image))
00121         return false;
00122 
00123     // The layers appear to be stored in top-to-bottom order. This is
00124     // the reverse of how a merged image must be computed. So, the layer
00125     // offsets are pushed onto a LIFO stack (thus, we don't have to load
00126     // all the data of all layers before beginning to construct the
00127     // merged image).
00128 
00129     QStack<qint32> layer_offsets;
00130 
00131     while (true) {
00132         qint32 layer_offset;
00133 
00134         xcf_io >> layer_offset;
00135 
00136         if (layer_offset == 0)
00137             break;
00138 
00139         layer_offsets.push(layer_offset);
00140     }
00141 
00142     xcf_image.num_layers = layer_offsets.size();
00143 
00144     if (layer_offsets.size() == 0) {
00145         kDebug(399) << "XCF: no layers!";
00146         return false;
00147     }
00148 
00149     // Load each layer and add it to the image
00150     while (!layer_offsets.isEmpty()) {
00151         qint32 layer_offset = layer_offsets.pop();
00152 
00153         xcf_io.device()->seek(layer_offset);
00154 
00155         if (!loadLayer(xcf_io, xcf_image))
00156             return false;
00157     }
00158 
00159     if (!xcf_image.initialized) {
00160         kDebug(399) << "XCF: no visible layers!";
00161         return false;
00162     }
00163 
00164         *outImage = xcf_image.image;
00165         return true;
00166 }
00167 
00168 
00176 bool XCFImageFormat::loadImageProperties(QDataStream& xcf_io, XCFImage& xcf_image)
00177 {
00178     while (true) {
00179         PropType type;
00180         QByteArray bytes;
00181 
00182         if (!loadProperty(xcf_io, type, bytes)) {
00183             kDebug(399) << "XCF: error loading global image properties";
00184             return false;
00185         }
00186 
00187         QDataStream property(bytes);
00188 
00189         switch (type) {
00190             case PROP_END:
00191                 return true;
00192 
00193             case PROP_COMPRESSION:
00194                 property >> xcf_image.compression;
00195                 break;
00196 
00197             case PROP_RESOLUTION:
00198                 property >> xcf_image.x_resolution >> xcf_image.y_resolution;
00199                 break;
00200 
00201             case PROP_TATTOO:
00202                 property >> xcf_image.tattoo;
00203                 break;
00204 
00205             case PROP_PARASITES:
00206                 while (!property.atEnd()) {
00207                     char* tag;
00208                     quint32 size;
00209 
00210                     property.readBytes(tag, size);
00211 
00212                     quint32 flags;
00213                     char* data=0;
00214                     property >> flags >> data;
00215 
00216                     if (tag && strncmp(tag, "gimp-comment", strlen("gimp-comment")) == 0)
00217                         xcf_image.image.setText("Comment", 0, data);
00218 
00219                     delete[] tag;
00220                     delete[] data;
00221                 }
00222                 break;
00223 
00224                 case PROP_UNIT:
00225                     property >> xcf_image.unit;
00226                     break;
00227 
00228                 case PROP_PATHS:    // This property is ignored.
00229                     break;
00230 
00231                 case PROP_USER_UNIT:    // This property is ignored.
00232                     break;
00233 
00234                 case PROP_COLORMAP:
00235                     property >> xcf_image.num_colors;
00236                                         if(xcf_image.num_colors < 0 || xcf_image.num_colors > 65535)
00237                                             return false;
00238 
00239                     xcf_image.palette.reserve(xcf_image.num_colors);
00240 
00241                     for (int i = 0; i < xcf_image.num_colors; i++) {
00242                         uchar r, g, b;
00243                         property >> r >> g >> b;
00244                         xcf_image.palette.push_back( qRgb(r,g,b) );
00245                     }
00246                     break;
00247 
00248                 default:
00249                     kDebug(399) << "XCF: unimplemented image property" << type
00250                             << ", size " << bytes.size() << endl;
00251         }
00252     }
00253 }
00254 
00255 
00263 bool XCFImageFormat::loadProperty(QDataStream& xcf_io, PropType& type, QByteArray& bytes)
00264 {
00265     quint32 foo;
00266     xcf_io >> foo;
00267     type=PropType(foo); // TODO urks
00268 
00269     char* data = 0;
00270     quint32 size;
00271 
00272     // The colormap property size is not the correct number of bytes:
00273     // The GIMP source xcf.c has size = 4 + ncolors, but it should be
00274     // 4 + 3 * ncolors
00275 
00276     if (type == PROP_COLORMAP) {
00277         xcf_io >> size;
00278                 quint32 ncolors;
00279                 xcf_io >> ncolors;
00280 
00281                 if(size > 65535 || size < 4)
00282                     return false;
00283 
00284         size = 3 * ncolors + 4;
00285         data = new char[size];
00286 
00287                 // since we already read "ncolors" from the stream, we put that data back
00288                 data[0] = 0;
00289                 data[1] = 0;
00290                 data[2] = ncolors >> 8;
00291                 data[3] = ncolors & 255;
00292 
00293                 // ... and read the remaining bytes from the stream
00294         xcf_io.readRawData(data + 4, size - 4);
00295     } else if (type == PROP_USER_UNIT) {
00296         // The USER UNIT property size is not correct. I'm not sure why, though.
00297         float factor;
00298         qint32 digits;
00299 
00300         xcf_io >> size >> factor >> digits;
00301 
00302         for (int i = 0; i < 5; i++) {
00303             char* unit_strings;
00304 
00305             xcf_io >> unit_strings;
00306 
00307             delete[] unit_strings;
00308 
00309             if (xcf_io.device()->atEnd()) {
00310                 kDebug(399) << "XCF: read failure on property " << type;
00311                 return false;
00312             }
00313         }
00314 
00315         size = 0;
00316     } else {
00317                 xcf_io >> size;
00318                 if(size >256000)
00319                     return false;
00320                 data = new char[size];
00321         xcf_io.readRawData(data, size);
00322         }
00323 
00324     if (size != 0 && data)
00325             bytes = QByteArray(data,size);
00326     
00327         delete [] data;
00328 
00329     return true;
00330 }
00331 
00332 
00341 bool XCFImageFormat::loadLayer(QDataStream& xcf_io, XCFImage& xcf_image)
00342 {
00343     Layer& layer(xcf_image.layer);
00344     delete[] layer.name;
00345 
00346     xcf_io >> layer.width >> layer.height >> layer.type >> layer.name;
00347 
00348     if (!loadLayerProperties(xcf_io, layer))
00349         return false;
00350 #if 0
00351   cout << "layer: \"" << layer.name << "\", size: " << layer.width << " x "
00352        << layer.height << ", type: " << layer.type << ", mode: " << layer.mode
00353        << ", opacity: " << layer.opacity << ", visible: " << layer.visible
00354        << ", offset: " << layer.x_offset << ", " << layer.y_offset << endl;
00355 #endif
00356   // Skip reading the rest of it if it is not visible. Typically, when
00357   // you export an image from the The GIMP it flattens (or merges) only
00358   // the visible layers into the output image.
00359 
00360     if (layer.visible == 0)
00361         return true;
00362 
00363     // If there are any more layers, merge them into the final QImage.
00364 
00365     xcf_io >> layer.hierarchy_offset >> layer.mask_offset;
00366 
00367     // Allocate the individual tile QImages based on the size and type
00368     // of this layer.
00369 
00370     if( !composeTiles(xcf_image))
00371         return false;
00372     xcf_io.device()->seek(layer.hierarchy_offset);
00373 
00374     // As tiles are loaded, they are copied into the layers tiles by
00375     // this routine. (loadMask(), below, uses a slightly different
00376     // version of assignBytes().)
00377 
00378     layer.assignBytes = assignImageBytes;
00379 
00380     if (!loadHierarchy(xcf_io, layer))
00381         return false;
00382 
00383     if (layer.mask_offset != 0) {
00384         xcf_io.device()->seek(layer.mask_offset);
00385 
00386         if (!loadMask(xcf_io, layer))
00387             return false;
00388     }
00389 
00390     // Now we should have enough information to initialize the final
00391     // QImage. The first visible layer determines the attributes
00392     // of the QImage.
00393 
00394     if (!xcf_image.initialized) {
00395         if( !initializeImage(xcf_image))
00396             return false;
00397         copyLayerToImage(xcf_image);
00398         xcf_image.initialized = true;
00399     } else
00400         mergeLayerIntoImage(xcf_image);
00401 
00402     return true;
00403 }
00404 
00405 
00413 bool XCFImageFormat::loadLayerProperties(QDataStream& xcf_io, Layer& layer)
00414 {
00415     while (true) {
00416         PropType type;
00417         QByteArray bytes;
00418 
00419         if (!loadProperty(xcf_io, type, bytes)) {
00420             kDebug(399) << "XCF: error loading layer properties";
00421             return false;
00422         }
00423 
00424         QDataStream property(bytes);
00425 
00426         switch (type) {
00427             case PROP_END:
00428                 return true;
00429 
00430             case PROP_ACTIVE_LAYER:
00431                 layer.active = true;
00432                 break;
00433 
00434             case PROP_OPACITY:
00435                 property >> layer.opacity;
00436                 break;
00437 
00438             case PROP_VISIBLE:
00439                 property >> layer.visible;
00440                 break;
00441 
00442             case PROP_LINKED:
00443                 property >> layer.linked;
00444                 break;
00445 
00446             case PROP_PRESERVE_TRANSPARENCY:
00447                 property >> layer.preserve_transparency;
00448                 break;
00449 
00450             case PROP_APPLY_MASK:
00451                 property >> layer.apply_mask;
00452                 break;
00453 
00454             case PROP_EDIT_MASK:
00455                 property >> layer.edit_mask;
00456                 break;
00457 
00458             case PROP_SHOW_MASK:
00459                 property >> layer.show_mask;
00460                 break;
00461 
00462             case PROP_OFFSETS:
00463                 property >> layer.x_offset >> layer.y_offset;
00464                 break;
00465 
00466             case PROP_MODE:
00467                 property >> layer.mode;
00468                 break;
00469 
00470             case PROP_TATTOO:
00471                 property >> layer.tattoo;
00472                 break;
00473 
00474             default:
00475                 kDebug(399) << "XCF: unimplemented layer property " << type
00476                         << ", size " << bytes.size() << endl;
00477         }
00478     }
00479 }
00480 
00481 
00487 bool XCFImageFormat::composeTiles(XCFImage& xcf_image)
00488 {
00489     Layer& layer(xcf_image.layer);
00490 
00491     layer.nrows = (layer.height + TILE_HEIGHT - 1) / TILE_HEIGHT;
00492     layer.ncols = (layer.width + TILE_WIDTH - 1) / TILE_WIDTH;
00493 
00494     layer.image_tiles.resize(layer.nrows);
00495 
00496     if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE)
00497         layer.alpha_tiles.resize(layer.nrows);
00498 
00499     if (layer.mask_offset != 0)
00500         layer.mask_tiles.resize(layer.nrows);
00501 
00502     for (uint j = 0; j < layer.nrows; j++) {
00503         layer.image_tiles[j].resize(layer.ncols);
00504 
00505         if (layer.type == GRAYA_GIMAGE || layer.type == INDEXEDA_GIMAGE)
00506             layer.alpha_tiles[j].resize(layer.ncols);
00507 
00508         if (layer.mask_offset != 0)
00509             layer.mask_tiles[j].resize(layer.ncols);
00510     }
00511 
00512     for (uint j = 0; j < layer.nrows; j++) {
00513         for (uint i = 0; i < layer.ncols; i++) {
00514 
00515             uint tile_width = (i + 1) * TILE_WIDTH <= layer.width
00516                     ? TILE_WIDTH : layer.width - i * TILE_WIDTH;
00517 
00518             uint tile_height = (j + 1) * TILE_HEIGHT <= layer.height
00519                     ? TILE_HEIGHT : layer.height - j * TILE_HEIGHT;
00520 
00521             // Try to create the most appropriate QImage (each GIMP layer
00522             // type is treated slightly differently)
00523 
00524             switch (layer.type) {
00525                 case RGB_GIMAGE:
00526                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_RGB32);
00527                     layer.image_tiles[j][i].setNumColors(0);
00528                     if( layer.image_tiles[j][i].isNull())
00529                         return false;
00530                     break;
00531 
00532                 case RGBA_GIMAGE:
00533                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_ARGB32);
00534                     layer.image_tiles[j][i].setNumColors(0);
00535                     if( layer.image_tiles[j][i].isNull())
00536                         return false;
00537                     break;
00538 
00539                 case GRAY_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                     break;
00546 
00547                 case GRAYA_GIMAGE:
00548                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00549                     layer.image_tiles[j][i].setNumColors(256);
00550                     if( layer.image_tiles[j][i].isNull())
00551                         return false;
00552                     setGrayPalette(layer.image_tiles[j][i]);
00553 
00554                     layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00555                     layer.alpha_tiles[j][i].setNumColors(256);
00556                     if( layer.alpha_tiles[j][i].isNull())
00557                         return false;
00558                     setGrayPalette(layer.alpha_tiles[j][i]);
00559                     break;
00560 
00561                 case INDEXED_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                     break;
00568 
00569                 case INDEXEDA_GIMAGE:
00570                     layer.image_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00571                     layer.image_tiles[j][i].setNumColors(xcf_image.num_colors);
00572                     if( layer.image_tiles[j][i].isNull())
00573                         return false;
00574                     setPalette(xcf_image, layer.image_tiles[j][i]);
00575 
00576                     layer.alpha_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00577                     layer.alpha_tiles[j][i].setNumColors(256);
00578                     if( layer.alpha_tiles[j][i].isNull())
00579                         return false;
00580                     setGrayPalette(layer.alpha_tiles[j][i]);
00581             }
00582 
00583             if (layer.mask_offset != 0) {
00584                 layer.mask_tiles[j][i] = QImage(tile_width, tile_height, QImage::Format_Indexed8);
00585                 layer.mask_tiles[j][i].setNumColors(256);
00586                 if( layer.mask_tiles[j][i].isNull())
00587                     return false;
00588                 setGrayPalette(layer.mask_tiles[j][i]);
00589             }
00590         }
00591     }
00592     return true;
00593 }
00594 
00595 
00602 void XCFImageFormat::setGrayPalette(QImage& image)
00603 {
00604     if (grayTable.isEmpty()) {
00605         grayTable.resize(256);
00606 
00607         for (int i = 0; i < 256; i++)
00608             grayTable[i] = qRgb(i, i, i);
00609     }
00610 
00611     image.setColorTable(grayTable);
00612 }
00613 
00614 
00620 void XCFImageFormat::setPalette(XCFImage& xcf_image, QImage& image)
00621 {
00622     Q_ASSERT (xcf_image.num_colors == xcf_image.palette.size());
00623 
00624     image.setColorTable(xcf_image.palette);
00625 }
00626 
00627 
00635 void XCFImageFormat::assignImageBytes(Layer& layer, uint i, uint j)
00636 {
00637     QImage &image = layer.image_tiles[j][i];
00638     uchar* tile = layer.tile;
00639     const int width = image.width();
00640     const int height = image.height();
00641     const int bytesPerLine = image.bytesPerLine();
00642     uchar *bits = image.bits();
00643 
00644     switch (layer.type) {
00645         case RGB_GIMAGE:
00646             for (int y = 0; y < height; y++) {
00647                 QRgb *dataPtr = (QRgb *) (bits + y * bytesPerLine);
00648                 for (int x = 0; x < width; x++) {
00649                     *dataPtr++ = qRgb(tile[0], tile[1], tile[2]);
00650                     tile += sizeof(QRgb);
00651                 }
00652             }
00653             break;
00654 
00655         case RGBA_GIMAGE:
00656             for (int y = 0; y < height; y++) {
00657                 QRgb *dataPtr = (QRgb *) (bits + y * bytesPerLine);
00658                 for (int x = 0; x < width; x++) {
00659                     *dataPtr++ =    qRgba(tile[0], tile[1], tile[2], tile[3]);
00660                     tile += sizeof(QRgb);
00661                 }
00662             }
00663             break;
00664 
00665         case GRAY_GIMAGE:
00666         case INDEXED_GIMAGE:
00667             for (int y = 0; y < height; y++) {
00668                 uchar *dataPtr = bits + y * bytesPerLine;
00669                 for (int x = 0; x < width; x++) {
00670                     *dataPtr++ = tile[0];
00671                     tile += sizeof(QRgb);
00672                 }
00673             }
00674             break;
00675 
00676         case GRAYA_GIMAGE:
00677         case INDEXEDA_GIMAGE:
00678             for (int y = 0; y < height; y++) {
00679                 uchar *dataPtr = bits + y * bytesPerLine;
00680                 uchar *alphaPtr = layer.alpha_tiles[j][i].scanLine(y);
00681                 for (int x = 0; x < width; x++) {
00682 
00683                 // The "if" here should not be necessary, but apparently there
00684                 // are some cases where the image can contain larger indices
00685                 // than there are colors in the palette. (A bug in The GIMP?)
00686 
00687                     if (tile[0] < image.numColors())
00688                         *dataPtr = tile[0];
00689 
00690                     *alphaPtr = tile[1];
00691                     dataPtr += 1;
00692                     alphaPtr += 1;
00693                     tile += sizeof(QRgb);
00694                 }
00695             }
00696             break;
00697     }
00698 }
00699 
00700 
00709 bool XCFImageFormat::loadHierarchy(QDataStream& xcf_io, Layer& layer)
00710 {
00711     qint32 width;
00712     qint32 height;
00713     qint32 bpp;
00714     quint32 offset;
00715 
00716     xcf_io >> width >> height >> bpp >> offset;
00717 
00718     // GIMP stores images in a "mipmap"-like format (multiple levels of
00719     // increasingly lower resolution). Only the top level is used here,
00720     // however.
00721 
00722     quint32 junk;
00723     do {
00724         xcf_io >> junk;
00725 
00726         if (xcf_io.device()->atEnd()) {
00727             kDebug(399) << "XCF: read failure on layer " << layer.name << " level offsets";
00728             return false;
00729         }
00730     } while (junk != 0);
00731 
00732     qint64 saved_pos = xcf_io.device()->pos();
00733 
00734     xcf_io.device()->seek(offset);
00735     if (!loadLevel(xcf_io, layer, bpp))
00736         return false;
00737 
00738     xcf_io.device()->seek(saved_pos);
00739     return true;
00740 }
00741 
00742 
00751 bool XCFImageFormat::loadLevel(QDataStream& xcf_io, Layer& layer, qint32 bpp)
00752 {
00753     qint32 width;
00754     qint32 height;
00755     quint32 offset;
00756 
00757     xcf_io >> width >> height >> offset;
00758 
00759     if (offset == 0)
00760         return true;
00761 
00762     for (uint j = 0; j < layer.nrows; j++) {
00763         for (uint i = 0; i < layer.ncols; i++) {
00764 
00765             if (offset == 0) {
00766                 kDebug(399) << "XCF: incorrect number of tiles in layer " << layer.name;
00767                 return false;
00768             }
00769 
00770             qint64 saved_pos = xcf_io.device()->pos();
00771             quint32 offset2;
00772             xcf_io >> offset2;
00773 
00774             // Evidently, RLE can occasionally expand a tile instead of compressing it!
00775 
00776             if (offset2 == 0)
00777                 offset2 = offset + (uint)(TILE_WIDTH * TILE_HEIGHT * 4 * 1.5);
00778 
00779             xcf_io.device()->seek(offset);
00780             int size = layer.image_tiles[j][i].width() * layer.image_tiles[j][i].height();
00781 
00782             if (!loadTileRLE(xcf_io, layer.tile, size, offset2 - offset, bpp))
00783                 return false;
00784 
00785             // The bytes in the layer tile are juggled differently depending on
00786             // the target QImage. The caller has set layer.assignBytes to the
00787             // appropriate routine.
00788 
00789             layer.assignBytes(layer, i, j);
00790 
00791             xcf_io.device()->seek(saved_pos);
00792             xcf_io >> offset;
00793         }
00794     }
00795 
00796     return true;
00797 }
00798 
00799 
00806 bool XCFImageFormat::loadMask(QDataStream& xcf_io, Layer& layer)
00807 {
00808     qint32 width;
00809     qint32 height;
00810     char* name;
00811 
00812     xcf_io >> width >> height >> name;
00813 
00814     delete name;
00815 
00816     if (!loadChannelProperties(xcf_io, layer))
00817         return false;
00818 
00819     quint32 hierarchy_offset;
00820     xcf_io >> hierarchy_offset;
00821 
00822     xcf_io.device()->seek(hierarchy_offset);
00823     layer.assignBytes = assignMaskBytes;
00824 
00825     if (!loadHierarchy(xcf_io, layer))
00826         return false;
00827 
00828     return true;
00829 }
00830 
00831 
00855 bool XCFImageFormat::loadTileRLE(QDataStream& xcf_io, uchar* tile, int image_size,
00856         int data_length, qint32 bpp)
00857 {
00858     uchar* data;
00859 
00860     uchar* xcfdata;
00861     uchar* xcfodata;
00862     uchar* xcfdatalimit;
00863 
00864     if (data_length < 0 || data_length > int(TILE_WIDTH * TILE_HEIGHT * 4 * 1.5)) {
00865         kDebug(399) << "XCF: invalid tile data length" << data_length;
00866         return false;
00867     }
00868 
00869     xcfdata = xcfodata = new uchar[data_length];
00870 
00871     xcf_io.readRawData((char*)xcfdata, data_length);
00872 
00873     if (!xcf_io.device()->isOpen()) {
00874         delete[] xcfodata;
00875         kDebug(399) << "XCF: read failure on tile";
00876         return false;
00877     }
00878 
00879     xcfdatalimit = &xcfodata[data_length - 1];
00880 
00881     for (int i = 0; i < bpp; ++i) {
00882 
00883         data = tile + i;
00884 
00885         int count = 0;
00886         int size = image_size;
00887 
00888         while (size > 0) {
00889             if (xcfdata > xcfdatalimit)
00890                 goto bogus_rle;
00891 
00892             uchar val = *xcfdata++;
00893             uint length = val;
00894 
00895             if (length >= 128) {
00896                 length = 255 - (length - 1);
00897                 if (length == 128) {
00898                     if (xcfdata >= xcfdatalimit)
00899                         goto bogus_rle;
00900 
00901                     length = (*xcfdata << 8) + xcfdata[1];
00902 
00903                     xcfdata += 2;
00904                 }
00905 
00906                 count += length;
00907                 size -= length;
00908 
00909                 if (size < 0)
00910                     goto bogus_rle;
00911 
00912                 if (&xcfdata[length - 1] > xcfdatalimit)
00913                     goto bogus_rle;
00914 
00915                 while (length-- > 0) {
00916                     *data = *xcfdata++;
00917                     data += sizeof(QRgb);
00918                 }
00919             } else {
00920                 length += 1;
00921                 if (length == 128) {
00922                     if (xcfdata >= xcfdatalimit)
00923                         goto bogus_rle;
00924 
00925                     length = (*xcfdata << 8) + xcfdata[1];
00926                     xcfdata += 2;
00927                 }
00928 
00929                 count += length;
00930                 size -= length;
00931 
00932                 if (size < 0)
00933                     goto bogus_rle;
00934 
00935                 if (xcfdata > xcfdatalimit)
00936                     goto bogus_rle;
00937 
00938                 val = *xcfdata++;
00939 
00940                 while (length-- > 0) {
00941                     *data = val;
00942                     data += sizeof(QRgb);
00943                 }
00944             }
00945         }
00946     }
00947 
00948     delete[] xcfodata;
00949     return true;
00950 
00951 bogus_rle:
00952 
00953     kDebug(399) << "The run length encoding could not be decoded properly";
00954     delete[] xcfodata;
00955     return false;
00956 }
00957 
00958 
00966 bool XCFImageFormat::loadChannelProperties(QDataStream& xcf_io, Layer& layer)
00967 {
00968     while (true) {
00969         PropType type;
00970         QByteArray bytes;
00971 
00972         if (!loadProperty(xcf_io, type, bytes)) {
00973             kDebug(399) << "XCF: error loading channel properties";
00974             return false;
00975         }
00976 
00977         QDataStream property(bytes);
00978 
00979         switch (type) {
00980             case PROP_END:
00981                 return true;
00982 
00983             case PROP_OPACITY:
00984                 property >> layer.mask_channel.opacity;
00985                 break;
00986 
00987             case PROP_VISIBLE:
00988                 property >> layer.mask_channel.visible;
00989                 break;
00990 
00991             case PROP_SHOW_MASKED:
00992                 property >> layer.mask_channel.show_masked;
00993                 break;
00994 
00995             case PROP_COLOR:
00996                 property >> layer.mask_channel.red >> layer.mask_channel.green
00997                         >> layer.mask_channel.blue;
00998                 break;
00999 
01000             case PROP_TATTOO:
01001                 property >> layer.mask_channel.tattoo;
01002                 break;
01003 
01004             default:
01005                 kDebug(399) << "XCF: unimplemented channel property " << type
01006                         << ", size " << bytes.size() << endl;
01007         }
01008     }
01009 }
01010 
01011 
01018 void XCFImageFormat::assignMaskBytes(Layer& layer, uint i, uint j)
01019 {
01020     QImage &image = layer.mask_tiles[j][i];
01021     uchar* tile = layer.tile;
01022     const int width = image.width();
01023     const int height = image.height();
01024     const int bytesPerLine = image.bytesPerLine();
01025     uchar *bits = image.bits();
01026 
01027     for (int y = 0; y < height; y++) {
01028         uchar *dataPtr = bits + y * bytesPerLine;
01029         for (int x = 0; x < width; x++) {
01030             *dataPtr++ = tile[0];
01031             tile += sizeof(QRgb);
01032         }
01033     }
01034 }
01035 
01036 
01065 bool XCFImageFormat::initializeImage(XCFImage& xcf_image)
01066 {
01067     // (Aliases to make the code look a little better.)
01068     Layer& layer(xcf_image.layer);
01069     QImage& image(xcf_image.image);
01070 
01071     switch (layer.type) {
01072         case RGB_GIMAGE:
01073             if (layer.opacity == OPAQUE_OPACITY) {
01074                 image = QImage( xcf_image.width, xcf_image.height, QImage::Format_RGB32);
01075                 if( image.isNull())
01076                     return false;
01077                 image.fill(qRgb(255, 255, 255));
01078                 break;
01079             } // else, fall through to 32-bit representation
01080 
01081         case RGBA_GIMAGE:
01082             image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01083             if( image.isNull())
01084                 return false;
01085             image.fill(qRgba(255, 255, 255, 0));
01086             break;
01087 
01088         case GRAY_GIMAGE:
01089             if (layer.opacity == OPAQUE_OPACITY) {
01090                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01091                 image.setNumColors(256);
01092                 if( image.isNull())
01093                     return false;
01094                 setGrayPalette(image);
01095                 image.fill(255);
01096                 break;
01097             } // else, fall through to 32-bit representation
01098 
01099         case GRAYA_GIMAGE:
01100             image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01101             if( image.isNull())
01102                 return false;
01103             image.fill(qRgba(255, 255, 255, 0));
01104             break;
01105 
01106         case INDEXED_GIMAGE:
01107             // As noted in the table above, there are quite a few combinations
01108             // which are possible with indexed images, depending on the
01109             // presence of transparency (note: not translucency, which is not
01110             // supported by The GIMP for indexed images) and the number of
01111             // individual colors.
01112 
01113             // Note: Qt treats a bitmap with a Black and White color palette
01114             // as a mask, so only the "on" bits are drawn, regardless of the
01115             // order color table entries. Otherwise (i.e., at least one of the
01116             // color table entries is not black or white), it obeys the one-
01117             // or two-color palette. Have to ask about this...
01118 
01119             if (xcf_image.num_colors <= 2) {
01120                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
01121                 image.setNumColors(xcf_image.num_colors);
01122                 if( image.isNull())
01123                     return false;
01124                 image.fill(0);
01125                 setPalette(xcf_image, image);
01126             } else if (xcf_image.num_colors <= 256) {
01127                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01128                 image.setNumColors(xcf_image.num_colors);
01129                 if( image.isNull())
01130                     return false;
01131                 image.fill(0);
01132                 setPalette(xcf_image, image);
01133             }
01134             break;
01135 
01136         case INDEXEDA_GIMAGE:
01137             if (xcf_image.num_colors == 1) {
01138                 // Plenty(!) of room to add a transparent color
01139                 xcf_image.num_colors++;
01140                 xcf_image.palette.resize(xcf_image.num_colors);
01141                 xcf_image.palette[1] = xcf_image.palette[0];
01142                 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
01143 
01144                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_MonoLSB);
01145                 image.setNumColors(xcf_image.num_colors);
01146                 if( image.isNull())
01147                     return false;
01148                 image.fill(0);
01149                 setPalette(xcf_image, image);
01150             } else if (xcf_image.num_colors < 256) {
01151                 // Plenty of room to add a transparent color
01152                 xcf_image.num_colors++;
01153                 xcf_image.palette.resize(xcf_image.num_colors);
01154                 for (int c = xcf_image.num_colors - 1; c >= 1; c--)
01155                     xcf_image.palette[c] = xcf_image.palette[c - 1];
01156 
01157                 xcf_image.palette[0] = qRgba(255, 255, 255, 0);
01158                 image = QImage( xcf_image.width, xcf_image.height, QImage::Format_Indexed8);
01159                 image.setNumColors(xcf_image.num_colors);
01160                 if( image.isNull())
01161                     return false;
01162                 image.fill(0);
01163                 setPalette(xcf_image, image);
01164             } else {
01165                 // No room for a transparent color, so this has to be promoted to
01166                 // true color. (There is no equivalent PNG representation output
01167                 // from The GIMP as of v1.2.)
01168                 image = QImage(xcf_image.width, xcf_image.height, QImage::Format_ARGB32);
01169                 if( image.isNull())
01170                     return false;
01171                 image.fill(qRgba(255, 255, 255, 0));
01172             }
01173             break;
01174     }
01175 
01176     image.setDotsPerMeterX((int)(xcf_image.x_resolution * INCHESPERMETER));
01177     image.setDotsPerMeterY((int)(xcf_image.y_resolution * INCHESPERMETER));
01178     return true;
01179 }
01180 
01181 
01187 void XCFImageFormat::copyLayerToImage(XCFImage& xcf_image)
01188 {
01189     Layer& layer(xcf_image.layer);
01190     QImage& image(xcf_image.image);
01191     PixelCopyOperation copy = 0;
01192 
01193     switch (layer.type) {
01194         case RGB_GIMAGE:
01195         case RGBA_GIMAGE:
01196             copy = copyRGBToRGB;
01197             break;
01198         case GRAY_GIMAGE:
01199             if (layer.opacity == OPAQUE_OPACITY)
01200                 copy = copyGrayToGray;
01201             else
01202                 copy = copyGrayToRGB;
01203             break;
01204         case GRAYA_GIMAGE:
01205             copy = copyGrayAToRGB;
01206             break;
01207         case INDEXED_GIMAGE:
01208             copy = copyIndexedToIndexed;
01209             break;
01210         case INDEXEDA_GIMAGE:
01211             if (xcf_image.image.depth() <= 8)
01212                 copy = copyIndexedAToIndexed;
01213             else
01214                 copy = copyIndexedAToRGB;
01215     }
01216 
01217     if (!copy) {
01218         return;
01219     }
01220 
01221     // For each tile...
01222 
01223     for (uint j = 0; j < layer.nrows; j++) {
01224         uint y = j * TILE_HEIGHT;
01225 
01226         for (uint i = 0; i < layer.ncols; i++) {
01227             uint x = i * TILE_WIDTH;
01228 
01229             // This seems the best place to apply the dissolve because it
01230             // depends on the global position of each tile's
01231             // pixels. Apparently it's the only mode which can apply to a
01232             // single layer.
01233 
01234             if (layer.mode == DISSOLVE_MODE) {
01235                 if (!random_table_initialized) {
01236                     initializeRandomTable();
01237                     random_table_initialized = true;
01238                 }
01239                 if (layer.type == RGBA_GIMAGE)
01240                     dissolveRGBPixels(layer.image_tiles[j][i], x, y);
01241 
01242                 else if (layer.type == GRAYA_GIMAGE)
01243                     dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
01244             }
01245 
01246             // Shortcut for common case
01247             if (copy == copyRGBToRGB && layer.apply_mask != 1) {
01248                 QPainter painter(&image);
01249                 painter.setOpacity(layer.opacity / 255.0);
01250                 painter.setCompositionMode(QPainter::CompositionMode_Source);
01251                 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
01252                 continue;
01253             }
01254 
01255             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01256                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01257 
01258                     int m = x + k + layer.x_offset;
01259                     int n = y + l + layer.y_offset;
01260 
01261                     if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
01262                         continue;
01263 
01264                     (*copy)(layer, i, j, k, l, image, m, n);
01265                 }
01266             }
01267         }
01268     }
01269 }
01270 
01271 
01285 void XCFImageFormat::copyRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
01286         QImage& image, int m, int n)
01287 {
01288     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01289     uchar src_a = layer.opacity;
01290 
01291     if (layer.type == RGBA_GIMAGE)
01292         src_a = INT_MULT(src_a, qAlpha(src));
01293 
01294     // Apply the mask (if any)
01295 
01296     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01297             layer.mask_tiles[j].size() > (int)i)
01298         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01299 
01300     image.setPixel(m, n, qRgba(src, src_a));
01301 }
01302 
01303 
01315 void XCFImageFormat::copyGrayToGray(Layer& layer, uint i, uint j, int k, int l,
01316         QImage& image, int m, int n)
01317 {
01318     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01319     image.setPixel(m, n, src);
01320 }
01321 
01322 
01336 void XCFImageFormat::copyGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
01337         QImage& image, int m, int n)
01338 {
01339     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01340     uchar src_a = layer.opacity;
01341     image.setPixel(m, n, qRgba(src, src_a));
01342 }
01343 
01344 
01358 void XCFImageFormat::copyGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
01359                       QImage& image, int m, int n)
01360 {
01361     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01362     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01363     src_a = INT_MULT(src_a, layer.opacity);
01364 
01365     // Apply the mask (if any)
01366 
01367     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01368             layer.mask_tiles[j].size() > (int)i)
01369         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01370 
01371     image.setPixel(m, n, qRgba(src, src_a));
01372 }
01373 
01374 
01386 void XCFImageFormat::copyIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
01387         QImage& image, int m, int n)
01388 {
01389     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01390     image.setPixel(m, n, src);
01391 }
01392 
01393 
01405 void XCFImageFormat::copyIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
01406         QImage& image, int m, int n)
01407 {
01408     uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
01409     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01410     src_a = INT_MULT(src_a, layer.opacity);
01411 
01412     if (layer.apply_mask == 1 &&
01413             layer.mask_tiles.size() > (int)j &&
01414             layer.mask_tiles[j].size() > (int)i)
01415         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01416 
01417     if (src_a > 127)
01418         src++;
01419     else
01420         src = 0;
01421 
01422 image.setPixel(m, n, src);
01423 }
01424 
01425 
01439 void XCFImageFormat::copyIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
01440         QImage& image, int m, int n)
01441 {
01442     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01443     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01444     src_a = INT_MULT(src_a, layer.opacity);
01445 
01446     // Apply the mask (if any)
01447     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01448             layer.mask_tiles[j].size() > (int)i)
01449         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01450 
01451     // This is what appears in the GIMP window
01452     if (src_a <= 127)
01453         src_a = 0;
01454     else
01455         src_a = OPAQUE_OPACITY;
01456 
01457     image.setPixel(m, n, qRgba(src, src_a));
01458 }
01459 
01460 
01465 void XCFImageFormat::mergeLayerIntoImage(XCFImage& xcf_image)
01466 {
01467     Layer& layer(xcf_image.layer);
01468     QImage& image(xcf_image.image);
01469 
01470     PixelMergeOperation merge = 0;
01471 
01472     if (!layer.opacity) return; // don't bother doing anything
01473 
01474     switch (layer.type) {
01475         case RGB_GIMAGE:
01476         case RGBA_GIMAGE:
01477             merge = mergeRGBToRGB;
01478             break;
01479         case GRAY_GIMAGE:
01480             if (layer.opacity == OPAQUE_OPACITY)
01481                 merge = mergeGrayToGray;
01482             else
01483                 merge = mergeGrayToRGB;
01484             break;
01485         case GRAYA_GIMAGE:
01486             if (xcf_image.image.depth() <= 8)
01487                 merge = mergeGrayAToGray;
01488             else
01489                 merge = mergeGrayAToRGB;
01490             break;
01491         case INDEXED_GIMAGE:
01492             merge = mergeIndexedToIndexed;
01493             break;
01494         case INDEXEDA_GIMAGE:
01495             if (xcf_image.image.depth() <= 8)
01496                 merge = mergeIndexedAToIndexed;
01497             else
01498                 merge = mergeIndexedAToRGB;
01499     }
01500 
01501     if (!merge) {
01502         return;
01503     }
01504 
01505     for (uint j = 0; j < layer.nrows; j++) {
01506         uint y = j * TILE_HEIGHT;
01507 
01508         for (uint i = 0; i < layer.ncols; i++) {
01509             uint x = i * TILE_WIDTH;
01510 
01511             // This seems the best place to apply the dissolve because it
01512             // depends on the global position of each tile's
01513             // pixels. Apparently it's the only mode which can apply to a
01514             // single layer.
01515 
01516             if (layer.mode == DISSOLVE_MODE) {
01517                 if (!random_table_initialized) {
01518                     initializeRandomTable();
01519                     random_table_initialized = true;
01520                 }
01521                 if (layer.type == RGBA_GIMAGE)
01522                     dissolveRGBPixels(layer.image_tiles[j][i], x, y);
01523 
01524                 else if (layer.type == GRAYA_GIMAGE)
01525                     dissolveAlphaPixels(layer.alpha_tiles[j][i], x, y);
01526             }
01527 
01528             // Shortcut for common case
01529             if (merge == mergeRGBToRGB && layer.apply_mask != 1
01530                 && layer.mode == NORMAL_MODE) {
01531                 QPainter painter(&image);
01532                 painter.setOpacity(layer.opacity / 255.0);
01533                 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
01534                 painter.drawImage(x + layer.x_offset, y + layer.y_offset, layer.image_tiles[j][i]);
01535                 continue;
01536             }
01537 
01538             for (int l = 0; l < layer.image_tiles[j][i].height(); l++) {
01539                 for (int k = 0; k < layer.image_tiles[j][i].width(); k++) {
01540 
01541                     int m = x + k + layer.x_offset;
01542                     int n = y + l + layer.y_offset;
01543 
01544                     if (m < 0 || m >= image.width() || n < 0 || n >= image.height())
01545                         continue;
01546 
01547                     (*merge)(layer, i, j, k, l, image, m, n);
01548                 }
01549             }
01550         }
01551     }
01552 }
01553 
01554 
01568 void XCFImageFormat::mergeRGBToRGB(Layer& layer, uint i, uint j, int k, int l,
01569         QImage& image, int m, int n)
01570 {
01571     QRgb src = layer.image_tiles[j][i].pixel(k, l);
01572     QRgb dst = image.pixel(m, n);
01573 
01574     uchar src_r = qRed(src);
01575     uchar src_g = qGreen(src);
01576     uchar src_b = qBlue(src);
01577     uchar src_a = qAlpha(src);
01578 
01579     uchar dst_r = qRed(dst);
01580     uchar dst_g = qGreen(dst);
01581     uchar dst_b = qBlue(dst);
01582     uchar dst_a = qAlpha(dst);
01583 
01584     if (!src_a) return; // nothing to merge
01585 
01586     switch (layer.mode) {
01587         case MULTIPLY_MODE: {
01588             src_r = INT_MULT(src_r, dst_r);
01589             src_g = INT_MULT(src_g, dst_g);
01590             src_b = INT_MULT(src_b, dst_b);
01591             src_a = qMin(src_a, dst_a);
01592             }
01593             break;
01594         case DIVIDE_MODE: {
01595             src_r = qMin((dst_r * 256) / (1 + src_r), 255);
01596             src_g = qMin((dst_g * 256) / (1 + src_g), 255);
01597             src_b = qMin((dst_b * 256) / (1 + src_b), 255);
01598             src_a = qMin(src_a, dst_a);
01599             }
01600             break;
01601         case SCREEN_MODE: {
01602             src_r = 255 - INT_MULT(255 - dst_r, 255 - src_r);
01603             src_g = 255 - INT_MULT(255 - dst_g, 255 - src_g);
01604             src_b = 255 - INT_MULT(255 - dst_b, 255 - src_b);
01605             src_a = qMin(src_a, dst_a);
01606             }
01607             break;
01608         case OVERLAY_MODE: {
01609             src_r = INT_MULT(dst_r, dst_r + INT_MULT(2 * src_r, 255 - dst_r));
01610             src_g = INT_MULT(dst_g, dst_g + INT_MULT(2 * src_g, 255 - dst_g));
01611             src_b = INT_MULT(dst_b, dst_b + INT_MULT(2 * src_b, 255 - dst_b));
01612             src_a = qMin(src_a, dst_a);
01613             }
01614             break;
01615         case DIFFERENCE_MODE: {
01616             src_r = dst_r > src_r ? dst_r - src_r : src_r - dst_r;
01617             src_g = dst_g > src_g ? dst_g - src_g : src_g - dst_g;
01618             src_b = dst_b > src_b ? dst_b - src_b : src_b - dst_b;
01619             src_a = qMin(src_a, dst_a);
01620             }
01621             break;
01622         case ADDITION_MODE: {
01623               src_r = add_lut(dst_r,src_r);
01624               src_g = add_lut(dst_g,src_g);
01625               src_b = add_lut(dst_b,src_b);
01626               src_a = qMin(src_a, dst_a);
01627             }
01628             break;
01629         case SUBTRACT_MODE: {
01630             src_r = dst_r > src_r ? dst_r - src_r : 0;
01631             src_g = dst_g > src_g ? dst_g - src_g : 0;
01632             src_b = dst_b > src_b ? dst_b - src_b : 0;
01633             src_a = qMin(src_a, dst_a);
01634             }
01635             break;
01636         case DARKEN_ONLY_MODE: {
01637             src_r = dst_r < src_r ? dst_r : src_r;
01638             src_g = dst_g < src_g ? dst_g : src_g;
01639             src_b = dst_b < src_b ? dst_b : src_b;
01640             src_a = qMin( src_a, dst_a );
01641             }
01642             break;
01643         case LIGHTEN_ONLY_MODE: {
01644             src_r = dst_r < src_r ? src_r : dst_r;
01645             src_g = dst_g < src_g ? src_g : dst_g;
01646             src_b = dst_b < src_b ? src_b : dst_b;
01647             src_a = qMin(src_a, dst_a);
01648             }
01649             break;
01650         case HUE_MODE: {
01651             uchar new_r = dst_r;
01652             uchar new_g = dst_g;
01653             uchar new_b = dst_b;
01654 
01655             RGBTOHSV(src_r, src_g, src_b);
01656             RGBTOHSV(new_r, new_g, new_b);
01657 
01658             new_r = src_r;
01659 
01660             HSVTORGB(new_r, new_g, new_b);
01661 
01662             src_r = new_r;
01663             src_g = new_g;
01664             src_b = new_b;
01665             src_a = qMin( src_a, dst_a );
01666             }
01667             break;
01668         case SATURATION_MODE: {
01669             uchar new_r = dst_r;
01670             uchar new_g = dst_g;
01671             uchar new_b = dst_b;
01672 
01673             RGBTOHSV(src_r, src_g, src_b);
01674             RGBTOHSV(new_r, new_g, new_b);
01675 
01676             new_g = src_g;
01677 
01678             HSVTORGB(new_r, new_g, new_b);
01679 
01680             src_r = new_r;
01681             src_g = new_g;
01682             src_b = new_b;
01683             src_a = qMin(src_a, dst_a);
01684             }
01685             break;
01686         case VALUE_MODE: {
01687             uchar new_r = dst_r;
01688             uchar new_g = dst_g;
01689             uchar new_b = dst_b;
01690 
01691             RGBTOHSV(src_r, src_g, src_b);
01692             RGBTOHSV(new_r, new_g, new_b);
01693 
01694             new_b = src_b;
01695 
01696             HSVTORGB(new_r, new_g, new_b);
01697 
01698             src_r = new_r;
01699             src_g = new_g;
01700             src_b = new_b;
01701             src_a = qMin(src_a, dst_a);
01702             }
01703             break;
01704         case COLOR_MODE: {
01705             uchar new_r = dst_r;
01706             uchar new_g = dst_g;
01707             uchar new_b = dst_b;
01708 
01709             RGBTOHLS(src_r, src_g, src_b);
01710             RGBTOHLS(new_r, new_g, new_b);
01711 
01712             new_r = src_r;
01713             new_b = src_b;
01714 
01715             HLSTORGB(new_r, new_g, new_b);
01716 
01717             src_r = new_r;
01718             src_g = new_g;
01719             src_b = new_b;
01720             src_a = qMin(src_a, dst_a);
01721             }
01722             break;
01723         case DODGE_MODE: {
01724             uint tmp;
01725 
01726             tmp = dst_r << 8;
01727             tmp /= 256 - src_r;
01728             src_r = (uchar) qMin(tmp, 255u);
01729 
01730             tmp = dst_g << 8;
01731             tmp /= 256 - src_g;
01732             src_g = (uchar) qMin(tmp, 255u);
01733 
01734             tmp = dst_b << 8;
01735             tmp /= 256 - src_b;
01736             src_b = (uchar) qMin(tmp, 255u);
01737 
01738             src_a = qMin(src_a, dst_a);
01739             }
01740             break;
01741         case BURN_MODE: {
01742             uint tmp;
01743 
01744             tmp = (255 - dst_r) << 8;
01745             tmp /= src_r + 1;
01746             src_r = (uchar) qMin(tmp, 255u);
01747             src_r = 255 - src_r;
01748 
01749             tmp = (255 - dst_g) << 8;
01750             tmp /= src_g + 1;
01751             src_g = (uchar) qMin(tmp, 255u);
01752             src_g = 255 - src_g;
01753 
01754             tmp = (255 - dst_b) << 8;
01755             tmp /= src_b + 1;
01756             src_b = (uchar) qMin(tmp, 255u);
01757             src_b = 255 - src_b;
01758 
01759             src_a = qMin(src_a, dst_a);
01760             }
01761             break;
01762         case HARDLIGHT_MODE: {
01763             uint tmp;
01764             if (src_r > 128) {
01765                 tmp = ((int)255-dst_r) * ((int) 255 - ((src_r-128) << 1));
01766                 src_r = (uchar) qMin(255 - (tmp >> 8), 255u);
01767             } else {
01768                 tmp = (int) dst_r * ((int) src_r << 1);
01769                 src_r = (uchar) qMin(tmp >> 8, 255u);
01770             }
01771 
01772             if (src_g > 128) {
01773                 tmp = ((int)255-dst_g) * ((int) 255 - ((src_g-128) << 1));
01774                 src_g = (uchar) qMin(255 - (tmp >> 8), 255u);
01775             } else {
01776                 tmp = (int) dst_g * ((int) src_g << 1);
01777                 src_g = (uchar) qMin(tmp >> 8, 255u);
01778             }
01779 
01780             if (src_b > 128) {
01781                 tmp = ((int)255-dst_b) * ((int) 255 - ((src_b-128) << 1));
01782                 src_b = (uchar) qMin(255 - (tmp >> 8), 255u);
01783             } else {
01784                 tmp = (int) dst_b * ((int) src_b << 1);
01785                 src_b = (uchar) qMin(tmp >> 8, 255u);
01786             }
01787             src_a = qMin(src_a, dst_a);
01788             }
01789             break;
01790         case SOFTLIGHT_MODE: {
01791             uint tmpS, tmpM;
01792 
01793             tmpM = INT_MULT(dst_r, src_r);
01794             tmpS = 255 - INT_MULT((255 - dst_r), (255-src_r));
01795             src_r = INT_MULT((255 - dst_r), tmpM)
01796                 + INT_MULT(dst_r, tmpS);
01797 
01798             tmpM = INT_MULT(dst_g, src_g);
01799             tmpS = 255 - INT_MULT((255 - dst_g), (255-src_g));
01800             src_g = INT_MULT((255 - dst_g), tmpM)
01801                 + INT_MULT(dst_g, tmpS);
01802 
01803             tmpM = INT_MULT(dst_b, src_b);
01804             tmpS = 255 - INT_MULT((255 - dst_b), (255-src_b));
01805             src_b = INT_MULT((255 - dst_b), tmpM)
01806                 + INT_MULT(dst_b, tmpS);
01807 
01808             src_a = qMin(src_a, dst_a);
01809             }
01810             break;
01811         case GRAIN_EXTRACT_MODE: {
01812             int tmp;
01813             
01814             tmp = dst_r - src_r + 128;
01815             tmp = qMin(tmp, 255);
01816             tmp = qMax(tmp, 0);
01817             src_r = (uchar) tmp;
01818 
01819             tmp = dst_g - src_g + 128;
01820             tmp = qMin(tmp, 255);
01821             tmp = qMax(tmp, 0);
01822             src_g = (uchar) tmp;
01823 
01824             tmp = dst_b - src_b + 128;
01825             tmp = qMin(tmp, 255);
01826             tmp = qMax(tmp, 0);
01827             src_b = (uchar) tmp;
01828 
01829             src_a = qMin(src_a, dst_a);
01830             }
01831             break;
01832         case GRAIN_MERGE_MODE: {
01833             int tmp;
01834             
01835             tmp = dst_r + src_r - 128;
01836             tmp = qMin(tmp, 255);
01837             tmp = qMax(tmp, 0);
01838             src_r = (uchar) tmp;
01839 
01840             tmp = dst_g + src_g - 128;
01841             tmp = qMin(tmp, 255);
01842             tmp = qMax(tmp, 0);
01843             src_g = (uchar) tmp;
01844 
01845             tmp = dst_b + src_b - 128;
01846             tmp = qMin(tmp, 255);
01847             tmp = qMax(tmp, 0);
01848             src_b = (uchar) tmp;
01849 
01850             src_a = qMin(src_a, dst_a);
01851             }
01852             break;
01853     }
01854 
01855     src_a = INT_MULT(src_a, layer.opacity);
01856 
01857     // Apply the mask (if any)
01858 
01859     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
01860             layer.mask_tiles[j].size() > (int)i)
01861         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
01862 
01863     uchar new_r, new_g, new_b, new_a;
01864     new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
01865 
01866     float src_ratio = (float)src_a / new_a;
01867     float dst_ratio = 1.0 - src_ratio;
01868 
01869     new_r = (uchar)(src_ratio * src_r + dst_ratio * dst_r + EPSILON);
01870     new_g = (uchar)(src_ratio * src_g + dst_ratio * dst_g + EPSILON);
01871     new_b = (uchar)(src_ratio * src_b + dst_ratio * dst_b + EPSILON);
01872 
01873     if (!layer_modes[layer.mode].affect_alpha)
01874         new_a = dst_a;
01875 
01876     image.setPixel(m, n, qRgba(new_r, new_g, new_b, new_a));
01877 }
01878 
01879 
01891 void XCFImageFormat::mergeGrayToGray(Layer& layer, uint i, uint j, int k, int l,
01892         QImage& image, int m, int n)
01893 {
01894     int src = layer.image_tiles[j][i].pixelIndex(k, l);
01895     image.setPixel(m, n, src);
01896 }
01897 
01898 
01910 void XCFImageFormat::mergeGrayAToGray(Layer& layer, uint i, uint j, int k, int l,
01911         QImage& image, int m, int n)
01912 {
01913     int src = qGray(layer.image_tiles[j][i].pixel(k, l));
01914     int dst = image.pixelIndex(m, n);
01915 
01916     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
01917 
01918     if (!src_a) return; // nothing to merge
01919 
01920     switch (layer.mode) {
01921         case MULTIPLY_MODE: {
01922                 src = INT_MULT( src, dst );
01923             }
01924             break;
01925         case DIVIDE_MODE: {
01926                 src = qMin((dst * 256) / (1 + src), 255);
01927             }
01928             break;
01929         case SCREEN_MODE: {
01930                 src = 255 - INT_MULT(255 - dst, 255 - src);
01931             }
01932             break;
01933         case OVERLAY_MODE: {
01934                 src = INT_MULT(dst, dst + INT_MULT(2 * src, 255 - dst));
01935             }
01936             break;
01937         case DIFFERENCE_MODE: {
01938                 src = dst > src ? dst - src : src - dst;
01939             }
01940             break;
01941         case ADDITION_MODE: {
01942                 src = add_lut(dst,src);
01943             }
01944             break;
01945         case SUBTRACT_MODE: {
01946                 src = dst > src ? dst - src : 0;
01947             }
01948             break;
01949         case DARKEN_ONLY_MODE: {
01950                 src = dst < src ? dst : src;
01951             }
01952             break;
01953         case LIGHTEN_ONLY_MODE: {
01954                 src = dst < src ? src : dst;
01955             }
01956             break;
01957         case DODGE_MODE: {
01958                 uint tmp = dst << 8;
01959                 tmp /= 256 - src;
01960                 src = (uchar) qMin(tmp, 255u);
01961             }
01962             break;
01963         case BURN_MODE: {
01964                 uint tmp = (255-dst) << 8;
01965                 tmp /= src + 1;
01966                 src = (uchar) qMin(tmp, 255u);
01967                 src = 255 - src;
01968             }
01969             break;
01970         case HARDLIGHT_MODE: {
01971                 uint tmp;
01972                 if (src > 128) {
01973                     tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1));
01974                     src = (uchar) qMin(255 - (tmp >> 8), 255u);
01975                 } else {
01976                     tmp = (int) dst * ((int) src << 1);
01977                     src = (uchar) qMin(tmp >> 8, 255u);
01978                 }
01979             }
01980             break;
01981         case SOFTLIGHT_MODE: {
01982                 uint tmpS, tmpM;
01983 
01984                 tmpM = INT_MULT(dst, src);
01985                 tmpS = 255 - INT_MULT((255-dst), (255-src));
01986                 src = INT_MULT((255 - dst), tmpM)
01987                     + INT_MULT(dst, tmpS);
01988 
01989             }
01990             break;
01991         case GRAIN_EXTRACT_MODE: {
01992                 int tmp;
01993                 
01994                 tmp = dst - src + 128;
01995                 tmp = qMin(tmp, 255);
01996                 tmp = qMax(tmp, 0);
01997 
01998                 src = (uchar) tmp;
01999             }
02000             break;
02001         case GRAIN_MERGE_MODE: {
02002                 int tmp;
02003                 
02004                 tmp = dst + src - 128;
02005                 tmp = qMin(tmp, 255);
02006                 tmp = qMax(tmp, 0);
02007 
02008                 src = (uchar) tmp;
02009             }
02010             break;
02011     }
02012 
02013     src_a = INT_MULT(src_a, layer.opacity);
02014 
02015     // Apply the mask (if any)
02016 
02017     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02018             layer.mask_tiles[j].size() > (int)i)
02019         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02020 
02021     uchar new_a = OPAQUE_OPACITY;
02022 
02023     float src_ratio = (float)src_a / new_a;
02024     float dst_ratio = 1.0 - src_ratio;
02025 
02026     uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
02027 
02028     image.setPixel(m, n, new_g);
02029 }
02030 
02031 
02045 void XCFImageFormat::mergeGrayToRGB(Layer& layer, uint i, uint j, int k, int l,
02046         QImage& image, int m, int n)
02047 {
02048     QRgb src = layer.image_tiles[j][i].pixel(k, l);
02049     uchar src_a = layer.opacity;
02050     image.setPixel(m, n, qRgba(src, src_a));
02051 }
02052 
02053 
02067 void XCFImageFormat::mergeGrayAToRGB(Layer& layer, uint i, uint j, int k, int l,
02068         QImage& image, int m, int n)
02069 {
02070     int src = qGray(layer.image_tiles[j][i].pixel(k, l));
02071     int dst = qGray(image.pixel(m, n));
02072 
02073     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02074     uchar dst_a = qAlpha(image.pixel(m, n));
02075 
02076     if (!src_a) return; // nothing to merge
02077 
02078     switch (layer.mode) {
02079         case MULTIPLY_MODE: {
02080                 src = INT_MULT(src, dst);
02081                 src_a = qMin(src_a, dst_a);
02082             }
02083             break;
02084         case DIVIDE_MODE: {
02085                 src = qMin((dst * 256) / (1 + src), 255);
02086                 src_a = qMin(src_a, dst_a);
02087             }
02088             break;
02089         case SCREEN_MODE: {
02090                 src = 255 - INT_MULT(255 - dst, 255 - src);
02091                 src_a = qMin(src_a, dst_a);
02092             }
02093             break;
02094         case OVERLAY_MODE: {
02095                 src = INT_MULT( dst, dst + INT_MULT(2 * src, 255 - dst));
02096                 src_a = qMin(src_a, dst_a);
02097             }
02098             break;
02099         case DIFFERENCE_MODE: {
02100                 src = dst > src ? dst - src : src - dst;
02101                 src_a = qMin(src_a, dst_a);
02102             }
02103             break;
02104         case ADDITION_MODE: {
02105                 src = add_lut(dst,src);
02106                 src_a = qMin(src_a, dst_a);
02107             }
02108             break;
02109         case SUBTRACT_MODE: {
02110                 src = dst > src ? dst - src : 0;
02111                 src_a = qMin(src_a, dst_a);
02112             }
02113             break;
02114         case DARKEN_ONLY_MODE: {
02115                 src = dst < src ? dst : src;
02116                 src_a = qMin(src_a, dst_a);
02117             }
02118             break;
02119         case LIGHTEN_ONLY_MODE: {
02120                 src = dst < src ? src : dst;
02121                 src_a = qMin(src_a, dst_a);
02122             }
02123             break;
02124         case DODGE_MODE: {
02125                 uint tmp = dst << 8;
02126                 tmp /= 256 - src;
02127                 src = (uchar) qMin(tmp, 255u);
02128                 src_a = qMin(src_a, dst_a);
02129             }
02130             break;
02131         case BURN_MODE: {
02132                 uint tmp = (255-dst) << 8;
02133                 tmp /= src + 1;
02134                 src = (uchar) qMin(tmp, 255u);
02135                 src = 255 - src;
02136                 src_a = qMin(src_a, dst_a);
02137             }
02138             break;
02139         case HARDLIGHT_MODE: {
02140                 uint tmp;
02141                 if (src > 128) {
02142                     tmp = ((int)255-dst) * ((int) 255 - ((src-128) << 1));
02143                     src = (uchar) qMin(255 - (tmp >> 8), 255u);
02144                 } else {
02145                     tmp = (int) dst * ((int) src << 1);
02146                     src = (uchar) qMin(tmp >> 8, 255u);
02147                 }
02148                 src_a = qMin(src_a, dst_a);
02149             }
02150             break;
02151         case SOFTLIGHT_MODE: {
02152                 uint tmpS, tmpM;
02153 
02154                 tmpM = INT_MULT(dst, src);
02155                 tmpS = 255 - INT_MULT((255 - dst), (255-src));
02156                 src = INT_MULT((255 - dst), tmpM)
02157                     + INT_MULT(dst, tmpS);
02158 
02159                 src_a = qMin(src_a, dst_a);
02160             }
02161             break;
02162         case GRAIN_EXTRACT_MODE: {
02163                 int tmp;
02164                 
02165                 tmp = dst - src + 128;
02166                 tmp = qMin(tmp, 255);
02167                 tmp = qMax(tmp, 0);
02168 
02169                 src = (uchar) tmp;
02170                 src_a = qMin(src_a, dst_a);
02171             }
02172             break;
02173         case GRAIN_MERGE_MODE: {
02174                 int tmp;
02175                 
02176                 tmp = dst + src - 128;
02177                 tmp = qMin(tmp, 255);
02178                 tmp = qMax(tmp, 0);
02179 
02180                 src = (uchar) tmp;
02181                 src_a = qMin(src_a, dst_a);
02182             }
02183             break;
02184     }
02185 
02186     src_a = INT_MULT(src_a, layer.opacity);
02187 
02188     // Apply the mask (if any)
02189     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02190             layer.mask_tiles[j].size() > (int)i)
02191         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02192 
02193     uchar new_a = dst_a + INT_MULT(OPAQUE_OPACITY - dst_a, src_a);
02194 
02195     float src_ratio = (float)src_a / new_a;
02196     float dst_ratio = 1.0 - src_ratio;
02197 
02198     uchar new_g = (uchar)(src_ratio * src + dst_ratio * dst + EPSILON);
02199 
02200     if (!layer_modes[layer.mode].affect_alpha)
02201         new_a = dst_a;
02202 
02203     image.setPixel(m, n, qRgba(new_g, new_g, new_g, new_a));
02204 }
02205 
02206 
02218 void XCFImageFormat::mergeIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l,
02219         QImage& image, int m, int n)
02220 {
02221     int src = layer.image_tiles[j][i].pixelIndex(k, l);
02222     image.setPixel(m, n, src);
02223 }
02224 
02225 
02237 void XCFImageFormat::mergeIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l,
02238         QImage& image, int m, int n)
02239 {
02240     uchar src = layer.image_tiles[j][i].pixelIndex(k, l);
02241     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02242     src_a = INT_MULT( src_a, layer.opacity );
02243 
02244     if ( layer.apply_mask == 1 &&
02245             layer.mask_tiles.size() > (int)j &&
02246             layer.mask_tiles[j].size() > (int)i)
02247         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02248 
02249     if (src_a > 127) {
02250         src++;
02251         image.setPixel(m, n, src);
02252     }
02253 }
02254 
02255 
02269 void XCFImageFormat::mergeIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l,
02270         QImage& image, int m, int n)
02271 {
02272     QRgb src = layer.image_tiles[j][i].pixel(k, l);
02273     uchar src_a = layer.alpha_tiles[j][i].pixelIndex(k, l);
02274     src_a = INT_MULT(src_a, layer.opacity);
02275 
02276     // Apply the mask (if any)
02277     if (layer.apply_mask == 1 && layer.mask_tiles.size() > (int)j &&
02278             layer.mask_tiles[j].size() > (int)i)
02279         src_a = INT_MULT(src_a, layer.mask_tiles[j][i].pixelIndex(k, l));
02280 
02281     // This is what appears in the GIMP window
02282     if (src_a <= 127)
02283         src_a = 0;
02284     else
02285         src_a = OPAQUE_OPACITY;
02286 
02287     image.setPixel(m, n, qRgba(src, src_a));
02288 }
02289 
02290 
02298 void XCFImageFormat::dissolveRGBPixels ( QImage& image, int x, int y )
02299 {
02300     // The apparently spurious rand() calls are to wind the random
02301     // numbers up to the same point for each tile.
02302 
02303     for (int l = 0; l < image.height(); l++) {
02304         srand(random_table[( l + y ) % RANDOM_TABLE_SIZE]);
02305 
02306         for (int k = 0; k < x; k++)
02307             rand();
02308 
02309         for (int k = 0; k < image.width(); k++) {
02310             int rand_val = rand() & 0xff;
02311             QRgb pixel = image.pixel(k, l);
02312 
02313             if (rand_val > qAlpha(pixel)) {
02314                 image.setPixel(k, l, qRgba(pixel, 0));
02315             }
02316         }
02317     }
02318 }
02319 
02320 
02330 void XCFImageFormat::dissolveAlphaPixels ( QImage& image, int x, int y )
02331 {
02332     // The apparently spurious rand() calls are to wind the random
02333     // numbers up to the same point for each tile.
02334 
02335     for (int l = 0; l < image.height(); l++) {
02336         srand( random_table[(l + y) % RANDOM_TABLE_SIZE]);
02337 
02338         for (int k = 0; k < x; k++)
02339             rand();
02340 
02341         for (int k = 0; k < image.width(); k++) {
02342             int rand_val = rand() & 0xff;
02343             uchar alpha = image.pixelIndex(k, l);
02344 
02345             if (rand_val > alpha) {
02346                 image.setPixel(k, l, 0);
02347             }
02348         }
02349     }
02350 }
02351 
02352 
02354 
02355 XCFHandler::XCFHandler()
02356 {
02357 }
02358 
02359 bool XCFHandler::canRead() const
02360 {
02361     if (canRead(device())) {
02362         setFormat("xcf");
02363         return true;
02364     }
02365     return false;
02366 }
02367 
02368 bool XCFHandler::read(QImage *image)
02369 {
02370     XCFImageFormat xcfif;
02371     return xcfif.readXCF(device(), image);
02372 }
02373 
02374 bool XCFHandler::write(const QImage &)
02375 {
02376     return false;
02377 }
02378 
02379 QByteArray XCFHandler::name() const
02380 {
02381     return "xcf";
02382 }
02383 
02384 bool XCFHandler::canRead(QIODevice *device)
02385 {
02386       if (!device) {
02387         qWarning("DDSHandler::canRead() called with no device");
02388         return false;
02389     }
02390 
02391     qint64 oldPos = device->pos();
02392 
02393     char head[8];
02394     qint64 readBytes = device->read(head, sizeof(head));
02395     if (readBytes != sizeof(head)) {
02396         if (device->isSequential()) {
02397             while (readBytes > 0)
02398                 device->ungetChar(head[readBytes-- - 1]);
02399         } else {
02400             device->seek(oldPos);
02401         }
02402         return false;
02403     }
02404 
02405     if (device->isSequential()) {
02406         while (readBytes > 0)
02407             device->ungetChar(head[readBytes-- - 1]);
02408     } else {
02409         device->seek(oldPos);
02410     }
02411 
02412     return qstrncmp(head, "gimp xcf", 8) == 0;
02413 }
02414 
02415 
02416 class XCFPlugin : public QImageIOPlugin
02417 {
02418 public:
02419     QStringList keys() const;
02420     Capabilities capabilities(QIODevice *device, const QByteArray &format) const;
02421     QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const;
02422 };
02423 
02424 QStringList XCFPlugin::keys() const
02425 {
02426     return QStringList() << "xcf" << "XCF";
02427 }
02428 
02429 QImageIOPlugin::Capabilities XCFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
02430 {
02431     if (format == "xcf" || format == "XCF")
02432         return Capabilities(CanRead);
02433     if (!format.isEmpty())
02434         return 0;
02435     if (!device->isOpen())
02436         return 0;
02437 
02438     Capabilities cap;
02439     if (device->isReadable() && XCFHandler::canRead(device))
02440         cap |= CanRead;
02441     return cap;
02442 }
02443 
02444 QImageIOHandler *XCFPlugin::create(QIODevice *device, const QByteArray &format) const
02445 {
02446     QImageIOHandler *handler = new XCFHandler;
02447     handler->setDevice(device);
02448     handler->setFormat(format);
02449     return handler;
02450 }
02451 
02452 Q_EXPORT_STATIC_PLUGIN(XCFPlugin)
02453 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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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