File indexing completed on 2024-05-19 04:35:16
0001 /* 0002 SPDX-FileCopyrightText: 2008 Tobias Koenig <tokoe@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "faxdocument.h" 0007 0008 #include <stdlib.h> 0009 0010 #include <QFile> 0011 0012 #include "faxexpand.h" 0013 0014 static const char FAXMAGIC[] = "\000PC Research, Inc\000\000\000\000\000\000"; 0015 0016 #define FAX_DPI_FINE QPoint(203, 196) 0017 0018 /* rearrange input bits into t16bits lsb-first chunks */ 0019 static void normalize(pagenode *pn, int revbits, int swapbytes, size_t length) 0020 { 0021 t32bits *p = reinterpret_cast<t32bits *>(pn->data); 0022 0023 switch ((revbits << 1) | swapbytes) { 0024 case 0: 0025 break; 0026 case 1: 0027 for (; length; length -= 4) { 0028 t32bits t = *p; 0029 *p++ = ((t & 0xff00ff00) >> 8) | ((t & 0x00ff00ff) << 8); 0030 } 0031 break; 0032 case 2: 0033 for (; length; length -= 4) { 0034 t32bits t = *p; 0035 t = ((t & 0xf0f0f0f0) >> 4) | ((t & 0x0f0f0f0f) << 4); 0036 t = ((t & 0xcccccccc) >> 2) | ((t & 0x33333333) << 2); 0037 *p++ = ((t & 0xaaaaaaaa) >> 1) | ((t & 0x55555555) << 1); 0038 } 0039 break; 0040 case 3: 0041 for (; length; length -= 4) { 0042 t32bits t = *p; 0043 t = ((t & 0xff00ff00) >> 8) | ((t & 0x00ff00ff) << 8); 0044 t = ((t & 0xf0f0f0f0) >> 4) | ((t & 0x0f0f0f0f) << 4); 0045 t = ((t & 0xcccccccc) >> 2) | ((t & 0x33333333) << 2); 0046 *p++ = ((t & 0xaaaaaaaa) >> 1) | ((t & 0x55555555) << 1); 0047 } 0048 } 0049 } 0050 0051 static bool new_image(pagenode *pn, int width, int height) 0052 { 0053 pn->image = QImage(width, height, QImage::Format_MonoLSB); 0054 pn->image.setColor(0, qRgb(255, 255, 255)); 0055 pn->image.setColor(1, qRgb(0, 0, 0)); 0056 pn->bytes_per_line = pn->image.bytesPerLine(); 0057 pn->dpi = FAX_DPI_FINE; 0058 pn->imageData = new uchar[width * height]; 0059 0060 return !pn->image.isNull(); 0061 } 0062 0063 /* get compressed data into memory */ 0064 static unsigned char *getstrip(pagenode *pn, int strip) 0065 { 0066 size_t offset, roundup; 0067 unsigned char *data; 0068 0069 union { 0070 t16bits s; 0071 unsigned char b[2]; 0072 } so; 0073 #define ShortOrder so.b[1] 0074 so.s = 1; /* XXX */ 0075 0076 QFile file(pn->filename); 0077 if (!file.open(QIODevice::ReadOnly)) { 0078 return nullptr; 0079 } 0080 0081 if (pn->strips == nullptr) { 0082 offset = 0; 0083 pn->length = file.size(); 0084 } else if (strip < pn->nstrips) { 0085 offset = pn->strips[strip].offset; 0086 pn->length = pn->strips[strip].size; 0087 } else { 0088 return nullptr; 0089 } 0090 0091 /* round size to full boundary plus t32bits */ 0092 roundup = (pn->length + 7) & ~3; 0093 0094 data = new uchar[roundup]; 0095 /* clear the last 2 t32bits, to force the expander to terminate 0096 even if the file ends in the middle of a fax line */ 0097 *(reinterpret_cast<t32bits *>(data + roundup / 4 - 2)) = 0; 0098 *(reinterpret_cast<t32bits *>(data + roundup / 4 - 1)) = 0; 0099 0100 /* we expect to get it in one gulp... */ 0101 if (!file.seek(offset) || (size_t)file.read((char *)data, pn->length) != pn->length) { 0102 delete[] data; 0103 return nullptr; 0104 } 0105 file.close(); 0106 0107 pn->data = reinterpret_cast<t16bits *>(data); 0108 0109 if (pn->strips == nullptr && memcmp(data, FAXMAGIC, sizeof(FAXMAGIC) - 1) == 0) { 0110 /* handle ghostscript / PC Research fax file */ 0111 pn->length -= 64; 0112 pn->vres = data[29]; 0113 pn->data += 32; 0114 roundup -= 64; 0115 } 0116 0117 normalize(pn, !pn->lsbfirst, ShortOrder, roundup); 0118 if (pn->size.height() == 0) { 0119 pn->size.setHeight(G3count(pn, pn->expander == g32expand)); 0120 } 0121 0122 if (pn->size.height() == 0) { 0123 delete[] data; 0124 pn->data = nullptr; 0125 return nullptr; 0126 } 0127 0128 if (pn->strips == nullptr) { 0129 pn->rowsperstrip = pn->size.height(); 0130 } 0131 0132 pn->dataOrig = reinterpret_cast<t16bits *>(data); 0133 0134 return data; 0135 } 0136 0137 static void draw_line(pixnum *run, int lineNum, pagenode *pn) 0138 { 0139 t32bits *p, *p1; /* p - current line, p1 - low-res duplicate */ 0140 pixnum *r; /* pointer to run-lengths */ 0141 t32bits pix; /* current pixel value */ 0142 t32bits acc; /* pixel accumulator */ 0143 int nacc; /* number of valid bits in acc */ 0144 int tot; /* total pixels in line */ 0145 int n; 0146 0147 lineNum += pn->stripnum * pn->rowsperstrip; 0148 if (lineNum >= pn->size.height()) { 0149 return; 0150 } 0151 0152 p = reinterpret_cast<t32bits *>(pn->imageData + lineNum * (2 - pn->vres) * pn->bytes_per_line); 0153 p1 = reinterpret_cast<t32bits *>(pn->vres ? nullptr : p + pn->bytes_per_line / sizeof(*p)); 0154 0155 r = run; 0156 acc = 0; 0157 nacc = 0; 0158 pix = pn->inverse ? ~0 : 0; 0159 tot = 0; 0160 while (tot < pn->size.width()) { 0161 n = *r++; 0162 tot += n; 0163 /* Watch out for buffer overruns, e.g. when n == 65535. */ 0164 if (tot > pn->size.width()) { 0165 break; 0166 } 0167 if (pix) { 0168 acc |= (~(t32bits)0 >> nacc); 0169 } else if (nacc) { 0170 acc &= (~(t32bits)0 << (32 - nacc)); 0171 } else { 0172 acc = 0; 0173 } 0174 if (nacc + n < 32) { 0175 nacc += n; 0176 pix = ~pix; 0177 continue; 0178 } 0179 *p++ = acc; 0180 if (p1) { 0181 *p1++ = acc; 0182 } 0183 n -= 32 - nacc; 0184 while (n >= 32) { 0185 n -= 32; 0186 *p++ = pix; 0187 if (p1) { 0188 *p1++ = pix; 0189 } 0190 } 0191 acc = pix; 0192 nacc = n; 0193 pix = ~pix; 0194 } 0195 if (nacc) { 0196 *p++ = acc; 0197 if (p1) { 0198 *p1++ = acc; 0199 } 0200 } 0201 } 0202 0203 static bool get_image(pagenode *pn) 0204 { 0205 unsigned char *data = getstrip(pn, 0); 0206 if (!data) { 0207 return false; 0208 } 0209 0210 if (!new_image(pn, pn->size.width(), (pn->vres ? 1 : 2) * pn->size.height())) { 0211 return false; 0212 } 0213 0214 (*pn->expander)(pn, draw_line); 0215 0216 return true; 0217 } 0218 0219 class FaxDocument::Private 0220 { 0221 public: 0222 explicit Private(FaxDocument *parent) 0223 : mParent(parent) 0224 { 0225 mPageNode.size = QSize(1728, 0); 0226 } 0227 0228 FaxDocument *mParent; 0229 pagenode mPageNode; 0230 FaxDocument::DocumentType mType; 0231 }; 0232 0233 FaxDocument::FaxDocument(const QString &fileName, DocumentType type) 0234 : d(new Private(this)) 0235 { 0236 d->mPageNode.filename = fileName; 0237 d->mPageNode.strips = nullptr; 0238 d->mPageNode.stripnum = 0; 0239 d->mPageNode.lsbfirst = 0; 0240 d->mPageNode.vres = 1; 0241 d->mPageNode.inverse = 0; 0242 d->mPageNode.data = nullptr; 0243 d->mPageNode.dataOrig = nullptr; 0244 d->mPageNode.imageData = nullptr; 0245 d->mType = type; 0246 0247 if (d->mType == G3) { 0248 d->mPageNode.expander = g31expand; // or g32expand?!? 0249 } else if (d->mType == G4) { 0250 d->mPageNode.expander = g4expand; 0251 } 0252 } 0253 0254 FaxDocument::~FaxDocument() 0255 { 0256 delete[] d->mPageNode.dataOrig; 0257 delete[] d->mPageNode.imageData; 0258 delete d; 0259 } 0260 0261 bool FaxDocument::load() 0262 { 0263 fax_init_tables(); 0264 0265 bool ok = get_image(&(d->mPageNode)); 0266 if (!ok) { 0267 return false; 0268 } 0269 0270 // byte-swapping the image 0271 int height = d->mPageNode.size.height(); 0272 int bytes_per_line = d->mPageNode.size.width() / 8; 0273 0274 QByteArray bytes(height * bytes_per_line, 0); 0275 for (int y = height - 1; y >= 0; --y) { 0276 quint32 offset = y * bytes_per_line; 0277 quint32 *source = reinterpret_cast<quint32 *>(d->mPageNode.imageData + offset); 0278 quint32 *dest = reinterpret_cast<quint32 *>(bytes.data() + offset); 0279 for (int x = (bytes_per_line / 4) - 1; x >= 0; --x) { 0280 quint32 dv = 0, sv = *source; 0281 for (int bit = 32; bit > 0; --bit) { 0282 dv <<= 1; 0283 dv |= sv & 1; 0284 sv >>= 1; 0285 } 0286 *dest = dv; 0287 ++dest; 0288 ++source; 0289 } 0290 } 0291 0292 // convert it into a QImage 0293 QImage img((uchar *)bytes.data(), d->mPageNode.size.width(), d->mPageNode.size.height(), QImage::Format_MonoLSB); 0294 img.setColor(0, qRgb(255, 255, 255)); 0295 img.setColor(1, qRgb(0, 0, 0)); 0296 0297 d->mPageNode.image = img.copy().scaled(img.width(), img.height() * 1.5); 0298 0299 return true; 0300 } 0301 0302 QImage FaxDocument::image() const 0303 { 0304 return d->mPageNode.image; 0305 }