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)
KDE 4.6 API Reference