File indexing completed on 2024-05-12 16:06:41

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 }