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