KImgIO
pic_read.cpp
Go to the documentation of this file.
00001 00021 /* This code is based on the GIMP-PIC plugin by Halfdan Ingvarsson, 00022 * and relicensed from GPL to LGPL to accomodate the KDE licensing policy 00023 * with his permission. 00024 * These is the original copyright: 00025 * Copyright (C) 1998 Halfdan Ingvarsson 00026 */ 00027 00028 #include "pic_rw.h" 00029 #include <netinet/in.h> 00030 #include <iostream> 00031 #include <qimage.h> 00032 #include <algorithm> 00033 00041 bool picReadHeader(QIODevice *dev, PICHeader *hdr, bool peek) { 00042 int result = 0; 00043 if (peek) { 00044 result = dev->peek((char*) hdr, HEADER_SIZE); 00045 } else { 00046 result = dev->read((char*) hdr, HEADER_SIZE); 00047 } 00048 00049 hdr->magic = ntohl(hdr->magic); 00050 hdr->width = ntohs(hdr->width); 00051 hdr->height = ntohs(hdr->height); 00052 hdr->fields = ntohs(hdr->fields); 00053 00054 if (hdr->magic != PIC_MAGIC_NUMBER || strncmp(hdr->id, "PICT", 4)) { 00055 return false; 00056 } 00057 00058 return result == HEADER_SIZE; 00059 } 00060 00061 #define CHANNEL_BYTE(ch, mask) (( ch & mask) ? 1 : 0) 00062 00068 static int channels2bpp(char channels) { 00069 return CHANNEL_BYTE(channels, RED) 00070 + CHANNEL_BYTE(channels, GREEN) 00071 + CHANNEL_BYTE(channels, BLUE) 00072 + CHANNEL_BYTE(channels, ALPHA); 00073 } 00074 00081 static bool readChannels(QIODevice *dev, PICChannel *channels, int &bpp) { 00082 int c = 0; 00083 memset(channels, 0, sizeof ( PICChannel) *8); 00084 do { 00085 int result = dev->read((char*) & channels[c], CHANNEL_SIZE); 00086 if (result != CHANNEL_SIZE) { 00087 return false; 00088 } else { 00089 bpp += channels2bpp(channels[c].channel); 00090 c++; 00091 } 00092 } while (channels[c - 1].chained); 00093 return true; 00094 } 00095 00101 inline static void makeComponentMap(unsigned channel, unsigned char *cmap) { 00102 std::fill(cmap, cmap + 8, 0); 00103 00104 unsigned compos[] = {ALPHA, BLUE, GREEN, RED}; 00105 unsigned rgba[] = {3, 2, 1, 0}; 00106 unsigned pos = 0; 00107 for (unsigned compo = 0; compo < 4; compo++) { 00108 if (CHANNEL_BYTE(channel, compos[compo])) { 00109 cmap[pos++] = rgba[compo]; 00110 } 00111 } 00112 } 00113 00121 inline static void pic2RGBA(unsigned char *src_pixel, unsigned char *target_pixel, unsigned char *cmap, unsigned components) { 00122 for (unsigned i = 0; i < components; i++) { 00123 target_pixel[cmap[i]] = src_pixel[i]; 00124 } 00125 } 00126 00132 inline static unsigned getNumChannels(PICChannel *channels) { 00133 unsigned result = 0; 00134 for (unsigned i = 0; i < 8; i++) { 00135 if (channels[i].channel != 0) { 00136 result++; 00137 } else { 00138 return result; 00139 } 00140 } 00141 return result; 00142 } 00143 00152 static int decodeRLE(QIODevice *dev, void *row, unsigned max, unsigned bpp, unsigned channels) { 00153 unsigned char buf[512]; 00154 unsigned *ptr = (unsigned *) row; 00155 unsigned char component_map[8]; 00156 unsigned len = 0; 00157 00158 makeComponentMap(channels, component_map); 00159 00160 if (dev->read((char*) buf, 1) != 1) { 00161 return -1; 00162 } 00163 00164 /* If last bit is 1, then it is 2 to 127 repetitions */ 00165 if (buf[0] > 128) { 00166 len = buf[0] - 127; 00167 if (len > max) { 00168 return -1; 00169 } 00170 unsigned count = dev->read((char*) buf, bpp); 00171 if (count != bpp) { 00172 return -1; 00173 } 00174 for (unsigned i = 0; i < len; i++) { 00175 pic2RGBA(buf, (unsigned char*) (ptr + i), component_map, bpp); 00176 } 00177 } /* If the value is exactly 10000000, it means that it is more than 127 repetitions */ 00178 else if (buf[0] == 128) { 00179 unsigned count = dev->read((char*) buf, bpp + 2); 00180 if (count != bpp + 2) { 00181 return -1; 00182 } 00183 len = (buf[0] << 8) | buf[1]; 00184 if (len > max) { 00185 return -1; 00186 } 00187 for (unsigned i = 0; i < len; i++) { 00188 pic2RGBA(buf + 2, (unsigned char*) (ptr + i), component_map, bpp); 00189 } 00190 } 00191 else { 00192 len = buf[0] + 1; 00193 if (len > max) { 00194 return -1; 00195 } 00196 unsigned count = dev->read((char*) buf, len * bpp); 00197 if (count != len * bpp) { 00198 return -1; 00199 } 00200 for (unsigned i = 0; i < len; i++) { 00201 pic2RGBA(buf + (i * bpp), (unsigned char*) (ptr + i), component_map, bpp); 00202 } 00203 } 00204 return len; 00205 } 00206 00215 static bool readRow(QIODevice *dev, unsigned *row, unsigned width, PICChannel *channels) { 00216 for (int c = 0; channels[c].channel != 0; c++) { 00217 unsigned remain = width; 00218 unsigned bpp = channels2bpp(channels[c].channel); 00219 if (channels[c].type == (int) RLE) { 00220 unsigned *rowpos = row; 00221 while (remain > 0) { 00222 int readed = decodeRLE(dev, rowpos, remain, bpp, channels[c].channel); 00223 if (readed < 0) { 00224 return false; 00225 } 00226 remain -= readed; 00227 rowpos += readed; 00228 } 00229 } else { 00230 unsigned char component_map[8]; 00231 unsigned count = dev->read((char*) row, width * bpp); 00232 if (count != width * bpp) { 00233 return false; 00234 } 00235 00236 makeComponentMap(channels[c].channel, component_map); 00237 for (unsigned i = 0; i < width; i++) { 00238 pic2RGBA(((unsigned char*) row) + (i * bpp), (unsigned char*) (row + i), component_map, bpp); 00239 } 00240 } 00241 } 00242 return true; 00243 } 00244 00245 #define FAIL() { \ 00246 std::cout << "ERROR Reading PIC!" << std::endl; \ 00247 return; \ 00248 } 00249 00250 bool hasAlpha(PICChannel *channels) { 00251 int channel = 0; 00252 do { 00253 if (CHANNEL_BYTE(channels[channel].channel, ALPHA)) { 00254 return true; 00255 } 00256 channel++; 00257 } while (channels[channel - 1].chained); 00258 return false; 00259 } 00260 00264 void pic_read(QIODevice *dev, QImage *result) { 00265 PICHeader header; 00266 PICChannel channels[8]; 00267 int bpp = 0; 00268 if (!picReadHeader(dev, &header) || !readChannels(dev, channels, bpp)) { 00269 FAIL(); 00270 } 00271 QImage img(header.width, header.height, QImage::Format_ARGB32); 00272 00273 for (int r = 0; r < header.height; r++) { 00274 unsigned *row = (unsigned*) img.scanLine(r); 00275 std::fill(row, row + header.width, 0); 00276 if (!readRow(dev, row, header.width, channels)) { 00277 FAIL(); 00278 } 00279 } 00280 // img->setAlphaBuffer(hasAlpha(channels)); 00281 *result = img; 00282 }
KDE 4.6 API Reference