├── JPEG Filter Osnove JPEG kodiranja.pdf ├── JPEGImage.pro ├── JPEGImage.pro.user ├── JPEGImage_global.h ├── README.md ├── bitarray.cpp ├── bitarray.h ├── component.cpp ├── component.h ├── exportpicture.cpp ├── exportpicture.h ├── huffmanelementscount.cpp ├── huffmanelementscount.h ├── huffmantable.cpp ├── huffmantable.h ├── jpegdecode.cpp ├── jpegdecode.h ├── jpegencode.cpp ├── jpegencode.h ├── jpegimage.cpp ├── jpegimage.h ├── quantizationtable.cpp └── quantizationtable.h /JPEG Filter Osnove JPEG kodiranja.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirduran/jpeg-encoder-decoder/61b6dee27e64587651063a19b5653729bde0047f/JPEG Filter Osnove JPEG kodiranja.pdf -------------------------------------------------------------------------------- /JPEGImage.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2011-02-02T23:18:21 4 | # 5 | #------------------------------------------------- 6 | 7 | TARGET = JPEGImage 8 | TEMPLATE = lib 9 | 10 | DEFINES += JPEGIMAGE_LIBRARY 11 | 12 | SOURCES += jpegimage.cpp \ 13 | component.cpp \ 14 | huffmantable.cpp \ 15 | quantizationtable.cpp \ 16 | exportpicture.cpp \ 17 | huffmanelementscount.cpp \ 18 | jpegdecode.cpp \ 19 | jpegencode.cpp 20 | 21 | HEADERS += jpegimage.h\ 22 | JPEGImage_global.h \ 23 | component.h \ 24 | huffmantable.h \ 25 | quantizationtable.h \ 26 | exportpicture.h \ 27 | huffmanelementscount.h \ 28 | jpegdecode.h \ 29 | jpegencode.h 30 | 31 | CONFIG += plugin debug 32 | 33 | LIBS = -L../../imagelib-build-desktop/ -limagelib 34 | INCLUDEPATH += ../../imagelib/include 35 | DESTDIR = ../../etfshop-build-desktop/plugins 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /JPEGImage.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ProjectExplorer.Project.ActiveTarget 7 | 0 8 | 9 | 10 | ProjectExplorer.Project.EditorSettings 11 | 12 | true 13 | false 14 | true 15 | 16 | Cpp 17 | 18 | CppGlobal 19 | 20 | 21 | 22 | QmlJS 23 | 24 | QmlJSGlobal 25 | 26 | 27 | 2 28 | UTF-8 29 | false 30 | 4 31 | false 32 | true 33 | 1 34 | true 35 | 0 36 | true 37 | 0 38 | 8 39 | true 40 | 1 41 | true 42 | true 43 | true 44 | false 45 | 46 | 47 | 48 | ProjectExplorer.Project.PluginSettings 49 | 50 | 51 | 52 | ProjectExplorer.Project.Target.0 53 | 54 | Desktop 55 | Desktop 56 | {f3591e40-2b66-4ff0-9771-1b514e74b9b8} 57 | 0 58 | 0 59 | 0 60 | 61 | 62 | 63 | true 64 | qmake 65 | 66 | QtProjectManager.QMakeBuildStep 67 | false 68 | true 69 | 70 | false 71 | 72 | 73 | true 74 | Make 75 | 76 | Qt4ProjectManager.MakeStep 77 | false 78 | 79 | 80 | 81 | 2 82 | Build 83 | 84 | ProjectExplorer.BuildSteps.Build 85 | 86 | 87 | 88 | true 89 | Make 90 | 91 | Qt4ProjectManager.MakeStep 92 | true 93 | clean 94 | 95 | 96 | 1 97 | Clean 98 | 99 | ProjectExplorer.BuildSteps.Clean 100 | 101 | 2 102 | false 103 | 104 | Release 105 | 106 | Qt4ProjectManager.Qt4BuildConfiguration 107 | 0 108 | D:/proba/JPEGImage-build-Desktop-Release 109 | true 110 | 111 | 112 | 113 | 114 | true 115 | qmake 116 | 117 | QtProjectManager.QMakeBuildStep 118 | false 119 | true 120 | 121 | false 122 | 123 | 124 | true 125 | Make 126 | 127 | Qt4ProjectManager.MakeStep 128 | false 129 | 130 | 131 | 132 | 2 133 | Build 134 | 135 | ProjectExplorer.BuildSteps.Build 136 | 137 | 138 | 139 | true 140 | Make 141 | 142 | Qt4ProjectManager.MakeStep 143 | true 144 | clean 145 | 146 | 147 | 1 148 | Clean 149 | 150 | ProjectExplorer.BuildSteps.Clean 151 | 152 | 2 153 | false 154 | 155 | Debug 156 | 157 | Qt4ProjectManager.Qt4BuildConfiguration 158 | 2 159 | D:/proba/JPEGImage-build-Desktop-Debug 160 | true 161 | 162 | 2 163 | 164 | 165 | 0 166 | Deploy 167 | 168 | ProjectExplorer.BuildSteps.Deploy 169 | 170 | 1 171 | Deploy locally 172 | 173 | ProjectExplorer.DefaultDeployConfiguration 174 | 175 | 1 176 | 177 | true 178 | 179 | false 180 | false 181 | false 182 | false 183 | true 184 | 0.01 185 | 10 186 | true 187 | 25 188 | 189 | true 190 | valgrind 191 | 192 | 0 193 | 1 194 | 2 195 | 3 196 | 4 197 | 5 198 | 6 199 | 7 200 | 8 201 | 9 202 | 10 203 | 11 204 | 12 205 | 13 206 | 14 207 | 208 | 209 | 2 210 | 211 | false 212 | 213 | %{buildDir} 214 | Custom Executable 215 | 216 | ProjectExplorer.CustomExecutableRunConfiguration 217 | 3768 218 | true 219 | false 220 | false 221 | true 222 | 223 | 1 224 | 225 | 226 | 227 | ProjectExplorer.Project.TargetCount 228 | 1 229 | 230 | 231 | ProjectExplorer.Project.Updater.EnvironmentId 232 | {8f005bbf-6cd8-457a-97fe-c04fdfb55458} 233 | 234 | 235 | ProjectExplorer.Project.Updater.FileVersion 236 | 12 237 | 238 | 239 | -------------------------------------------------------------------------------- /JPEGImage_global.h: -------------------------------------------------------------------------------- 1 | #ifndef JPEGIMAGE_GLOBAL_H 2 | #define JPEGIMAGE_GLOBAL_H 3 | 4 | #include 5 | 6 | #if defined(JPEGIMAGE_LIBRARY) 7 | # define JPEGIMAGESHARED_EXPORT Q_DECL_EXPORT 8 | #else 9 | # define JPEGIMAGESHARED_EXPORT Q_DECL_IMPORT 10 | #endif 11 | 12 | #endif // JPEGIMAGE_GLOBAL_H 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jpeg-encoder-decoder 2 | 3 | I worked on this project during 2012. Group of students implemented image editing software called ETFShop where students collaborated in order to create Photoshop like software. My task was to implement JPEG encode and decoder that is able to open/read and write/save .jpeg images. 4 | 5 | Inside this library you will find my final report about JPEG endoding/decoding that can help you to understand how things are working. It is written in Bosnian language, but feel free to translate it. 6 | 7 | 8 | [Open PDF documentation](https://github.com/amirduran/jpeg-encoder-decoder/blob/master/JPEG%20Filter%20Osnove%20JPEG%20kodiranja.pdf) 9 | 10 | This is C++ implementation of the JPEG encoder and decoder. This library will save image object as JPEG file, but will also decode JPEG file into valid image object. 11 | 12 | Here you can see, step by step, how JPEG standard works, and how JPEG image is created. 13 | 14 | 15 | 16 | 17 | Copyright (c) <2012> Amir Duran 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software 22 | -------------------------------------------------------------------------------- /bitarray.cpp: -------------------------------------------------------------------------------- 1 | #include "bitarray.h" 2 | #include 3 | 4 | const unsigned long mask[32] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 5 | 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000, 0x80000, 6 | 0x100000, 0x200000, 0x400000, 0x800000, 0x1000000, 0x2000000, 0x4000000, 7 | 0x8000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000}; 8 | 9 | 10 | BitArray::BitArray() { 11 | bitArray.resize(1); 12 | position = 0; 13 | } 14 | 15 | BitArray::BitArray(int length) { 16 | int size; 17 | if (length % 32 == 0) size = length / 32; 18 | else size = length / 32 + 1; 19 | bitArray.resize(size); 20 | position = length % 32; 21 | } 22 | 23 | void BitArray::reserve(unsigned int length) { 24 | bitArray.reserve(length/4); 25 | } 26 | 27 | bool BitArray::operator[](int pos) const { 28 | return bitArray[pos/32][pos%32]; 29 | } 30 | 31 | std::bitset<32u>::reference BitArray::operator[](int pos) { 32 | return bitArray[pos/32][pos%32]; 33 | } 34 | 35 | void BitArray::push_back(bool element) { 36 | if (position != 32) bitArray[bitArray.size()-1][position++] = element; 37 | else { 38 | bitset<32> bitset(element); 39 | bitArray.push_back(bitset); 40 | position = 1; 41 | } 42 | } 43 | 44 | void BitArray::push_back(unsigned int element, int length, bitOrder order) { 45 | if (order == LSB) { 46 | for (int i=0; ipush_back(element & mask[i]); 47 | } 48 | else { 49 | element = reverse(element); 50 | for (int i=32-length; i<32; i++) this->push_back(element & mask[i]); 51 | } 52 | } 53 | 54 | unsigned long &BitArray::to_ulong(int i) { 55 | returnValue = bitArray[i].to_ulong(); 56 | return returnValue; 57 | } 58 | 59 | unsigned int BitArray::size() { 60 | return 32 * (bitArray.size()-1) + position; 61 | } 62 | 63 | unsigned int BitArray::byteSize() { 64 | return bitArray.size() * 4; 65 | } 66 | 67 | unsigned int BitArray::reverse(unsigned int number) { 68 | return (BitReverseTable[number & 0xff] << 24) | (BitReverseTable[(number >> 8) & 0xff] << 16) | (BitReverseTable[(number >> 16) & 0xff] << 8) | (BitReverseTable[(number >> 24) & 0xff]); 69 | } 70 | 71 | unsigned int BitArray::capacity() { 72 | return bitArray.capacity()*4; 73 | } 74 | -------------------------------------------------------------------------------- /bitarray.h: -------------------------------------------------------------------------------- 1 | #ifndef BITARRAY_H 2 | #define BITARRAY_H 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | /* Made this class because bitset don't support dynamic bit arrays. 10 | Construct a dynamic array of bitsets of 32 bits size. 32 bits, 11 | because to_ulong() works only for bitset of 32 bits size*/ 12 | 13 | const unsigned char BitReverseTable[256] = { 14 | 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 15 | 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 16 | 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 17 | 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, 18 | 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, 19 | 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, 20 | 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, 21 | 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, 22 | 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, 23 | 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, 24 | 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, 25 | 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, 26 | 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, 27 | 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, 28 | 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, 29 | 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF 30 | }; 31 | 32 | enum bitOrder {MSB, LSB}; 33 | 34 | class BitArray { 35 | vector > bitArray; 36 | int position; 37 | unsigned long returnValue; 38 | unsigned int reverse(unsigned int number); 39 | public: 40 | BitArray(); 41 | BitArray(int length); 42 | void reserve(unsigned int length); 43 | void push_back(bool element); 44 | void push_back(unsigned int element, int length, bitOrder b); 45 | bool operator[](int pos) const; 46 | bitset<32>::reference operator[](int pos); 47 | unsigned long &to_ulong(int i); 48 | unsigned int size(); 49 | unsigned int byteSize(); 50 | unsigned int capacity(); 51 | }; 52 | 53 | #endif // BITARRAY_H 54 | -------------------------------------------------------------------------------- /component.cpp: -------------------------------------------------------------------------------- 1 | #include "component.h" 2 | 3 | Component::Component(int id,int HFactor,int VFactor,int QTableID,QuantizationTable &table) : componentQuantizationTable(&table) 4 | { 5 | componentTableID=QTableID; 6 | this->componentID=id; 7 | this->HFactor=HFactor; 8 | this->VFactor=VFactor; 9 | 10 | } 11 | 12 | -------------------------------------------------------------------------------- /component.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPONENT_H 2 | #define COMPONENT_H 3 | #include "quantizationtable.h" 4 | 5 | class Component 6 | { 7 | public: 8 | int componentID; 9 | int HFactor; 10 | int VFactor; 11 | int HScale, VScale; 12 | int componentTableID; 13 | QuantizationTable *componentQuantizationTable; 14 | Component(int id,int HFactor,int VFactor,int QTableID,QuantizationTable &table); 15 | 16 | }; 17 | 18 | #endif // COMPONENT_H 19 | -------------------------------------------------------------------------------- /exportpicture.cpp: -------------------------------------------------------------------------------- 1 | #include "exportpicture.h" 2 | #include 3 | 4 | 5 | ExportPicture::ExportPicture():luminance(0,vector(0,0)),chrominanceCb(0,vector(0,0)),chrominanceCr(0,vector(0,0)),height(0),width(0),fileName(0){ 6 | 7 | 8 | 9 | } 10 | ExportPicture::ExportPicture(QImage &image, const QString &fileName):luminance(0,vector(0,0)),chrominanceCb(0,vector(0,0)),chrominanceCr(0,vector(0,0)),height(image.width()),width(image.height()),fileName(fileName){ 11 | 12 | //Picture dimensions must be devidible by 8. If they are not, then we have to set them upp 13 | if(height%8 == 0 && width%8 == 0){ 14 | luminance.resize(height); 15 | chrominanceCb.resize(height); 16 | chrominanceCr.resize(height); 17 | 18 | for(int i=0;i 7 | #include 8 | 9 | class ExportPicture 10 | { 11 | public: 12 | vector >luminance; 13 | vector >chrominanceCb; 14 | vector >chrominanceCr; 15 | int height; 16 | int width; 17 | const QString &fileName; 18 | ExportPicture(); 19 | ExportPicture(QImage &image, const QString &fileName); 20 | void fillVectorsWithData(QImage &image); 21 | void deleteDataFromMatrix(); 22 | 23 | }; 24 | 25 | #endif // EXPORTPICTURE_H 26 | -------------------------------------------------------------------------------- /huffmanelementscount.cpp: -------------------------------------------------------------------------------- 1 | #include "huffmanelementscount.h" 2 | 3 | HuffmanElementsCount::HuffmanElementsCount():elementsCodedWithCodeLengthBits(),codeLength(0) 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /huffmanelementscount.h: -------------------------------------------------------------------------------- 1 | #ifndef HUFFMANELEMENTSCOUNT_H 2 | #define HUFFMANELEMENTSCOUNT_H 3 | 4 | using namespace std; 5 | #include 6 | class HuffmanElementsCount 7 | { 8 | public: 9 | int codeLength; 10 | vectorelementsCodedWithCodeLengthBits; 11 | HuffmanElementsCount(); 12 | }; 13 | 14 | #endif // HUFFMANELEMENTSCOUNT_H 15 | -------------------------------------------------------------------------------- /huffmantable.cpp: -------------------------------------------------------------------------------- 1 | #include "huffmantable.h" 2 | 3 | /*HuffmanTable::HuffmanTable():luminanceDChuffmanCode(256,0xFFFFFFFF),luminanceAChuffmanCode(256,0xFFFFFFFF),chrominanceDChuffmanCode(256,0xFFFFFFFF),chrominanceAChuffmanCode(256,0xFFFFFFFF),luminanceDCHuffmanCodeLength(256,1000),luminanceAChuffmanCodeLength(256,1000), chrominanceDChuffmanCodeLength(256,1000),chrominanceAChuffmanCodeLength(256,1000) 4 | { 5 | //Initial value of 1000 for code length is logic. Why? 6 | //Because code length can NEVER reach value of 1000. If we put number<0 sometimes this if statement can be true, and we can have problems 7 | // if (currentDataLength>=huffmanTable.luminanceDCHuffmanCodeLength[i] && huffmanTable.luminanceDChuffmanCode[i] == data >> (currentDataLength-huffmanTable.luminanceDCHuffmanCodeLength[i])) { 8 | }*/ 9 | 10 | HuffmanTable::HuffmanTable(): codes(256,0xFFFFFFFF), codeLengths(256,1000) 11 | { 12 | //Initial value of 1000 for code length is logic. Why? 13 | //Because code length can NEVER reach value of 1000. If we put number<0 sometimes this if statement can be true, and we can have problems 14 | // if (currentDataLength>=huffmanTable.luminanceDCHuffmanCodeLength[i] && huffmanTable.luminanceDChuffmanCode[i] == data >> (currentDataLength-huffmanTable.luminanceDCHuffmanCodeLength[i])) { 15 | } 16 | 17 | 18 | /*void HuffmanTable::deleteUnnecessaryData() { 19 | for (int i=0; i 5 | 6 | using namespace std; 7 | 8 | class HuffmanTable { 9 | 10 | public: 11 | /* //Huffman table codes 12 | vector luminanceDChuffmanCode; 13 | vector luminanceAChuffmanCode; 14 | vector chrominanceDChuffmanCode; 15 | vector chrominanceAChuffmanCode; 16 | 17 | //Huffman table code lengths 18 | vector luminanceDCHuffmanCodeLength; 19 | vector luminanceAChuffmanCodeLength; 20 | vector chrominanceDChuffmanCodeLength; 21 | vector chrominanceAChuffmanCodeLength;*/ 22 | unsigned char tableID, tableClass; 23 | vector codes, codeLengths; 24 | HuffmanTable(); 25 | // static void deleteUnnecessaryData(); 26 | }; 27 | 28 | #endif // HUFFMANTABLE_H 29 | -------------------------------------------------------------------------------- /jpegdecode.cpp: -------------------------------------------------------------------------------- 1 | #include "jpegdecode.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define DEBUGLEVEL 1 9 | #define BENCHMARK 10 | 11 | 12 | const double pi=3.1415926535897932384626433832795; 13 | 14 | 15 | JpegDecode::JpegDecode(QFile &file) : zigZagStart(0), zigZagEnd(63), adobeColorModel(Format::YCBCR), losslessFormat(false) { 16 | 17 | this->imagePtr = ImageStatePtr(new ImageState(file.fileName())); 18 | if (!file.open(QFile::ReadOnly)) throw "Can't open file"; 19 | 20 | readFile(file); 21 | 22 | file.close(); 23 | 24 | #ifdef BENCHMARK 25 | cout << "Huffman "<activeLayer()->updatePreview(); 30 | } 31 | 32 | 33 | void JpegDecode::readFile(QFile &file) { 34 | unsigned char ff, marker; 35 | 36 | // Is this a JPEG file? 37 | file.read((char*)&ff, sizeof(ff)); 38 | file.read((char*)&marker, sizeof(ff)); 39 | if (ff != 0xff || marker != 0xd8) 40 | throw "This is not a JPEG file"; 41 | 42 | bool endOfImage=false; 43 | while (!file.atEnd()) { 44 | file.read((char*)&ff, sizeof(ff)); 45 | 46 | if (ff != 0xff) { 47 | qDebug() << "0xFF expected but found "<1 53 | cout << "Found marker "<1 164 | qDebug() << "Read metadata JFIF"; 165 | #endif 166 | 167 | unsigned char c1,c2; 168 | picture.read((char*)&c1, sizeof(c1)); 169 | picture.read((char*)&c2, sizeof(c2)); 170 | metadata["JFIF version"] = QString("%1.%2").arg(int(c1)).arg(int(c2)); 171 | 172 | picture.read((char*)&c1, sizeof(c1)); 173 | unsigned short res1, res2; 174 | picture.read((char*)&res1, sizeof(res1)); 175 | picture.read((char*)&res2, sizeof(res2)); 176 | res1 = qFromBigEndian(res1); 177 | res2 = qFromBigEndian(res2); 178 | 179 | if (c1==0) 180 | metadata["Resolution"] = QString("%1x%2 pixels").arg(res1).arg(res2); 181 | else if (c1==1) 182 | metadata["Resolution"] = QString("%1x%2 DPI").arg(res1).arg(res2); 183 | else if (c1==2) 184 | metadata["Resolution"] = QString("%1x%2 DPcm").arg(res1).arg(res2); 185 | 186 | picture.read((char*)&c1, sizeof(c1)); 187 | picture.read((char*)&c2, sizeof(c2)); 188 | if (c1>0 && c2>0) { 189 | metadata["Thumbnail format"] = "Uncompressed RGB"; 190 | metadata["Thumbnail size"] = QString("%1x%2").arg(int(c1)).arg(int(c2)); 191 | thumbnail.resize(3*c1*c2); 192 | for (int i=0; i < 3*c1*c2; i++) { 193 | picture.read((char*)&c1, sizeof(c1)); 194 | thumbnail[i] = c1; 195 | } 196 | } 197 | 198 | } 199 | 200 | 201 | void JpegDecode::readMetaDataJFXX(QFile &picture, int headerLength) { 202 | metadata["JPEG format"] = "JPEG/JFXX"; 203 | #if DEBUGLEVEL>1 204 | qDebug() << "Read metadata JXFF"; 205 | #endif 206 | 207 | unsigned char tf,tw,th; 208 | 209 | picture.read((char*)&tf, sizeof(tf)); 210 | if (tf==0x10) { 211 | metadata["Thumbnail format"] = "JPEG/JIF"; 212 | thumbnail.resize(headerLength - 1); // Substract the TF byte that was just read 213 | for (int i=0; i < headerLength - 1; i++) { 214 | picture.read((char*)&tw, sizeof(tw)); 215 | thumbnail[i] = tw; 216 | } 217 | 218 | } else { 219 | picture.read((char*)&tw, sizeof(tw)); 220 | picture.read((char*)&th, sizeof(th)); 221 | if (tw>0 && th>0) { 222 | metadata["Thumbnail size"] = QString("%1x%2").arg(int(tw)).arg(int(th)); 223 | int thumbLen = 0; 224 | if (tf==0x11) { 225 | metadata["Thumbnail format"] = "Paletted"; 226 | thumbLen = 768 + tw*th; 227 | } 228 | if (tf==0x11) { 229 | metadata["Thumbnail format"] = "Uncompressed RGB"; 230 | thumbLen = 3*tw*th; 231 | } 232 | for (int i=0; i < thumbLen; i++) { 233 | picture.read((char*)&tw, sizeof(tw)); 234 | thumbnail[i] = tw; 235 | } 236 | } 237 | } 238 | } 239 | 240 | 241 | void JpegDecode::readAppSegment(QFile &picture, unsigned char marker) { 242 | // Known application segments 243 | // http://www.ozhiker.com/electronics/pjmt/jpeg_info/app_segments.html 244 | // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/JPEG.html 245 | const char* segmentTypes[11] = { "JFIF", "JFXX", "EXIF", "Exif", "http://ns.adobe.com/xap/1.0/", "ICC_PROFILE", "META", "Meta", "Ducky", "Photoshop 3.0", "Adobe" }; 246 | char data[30]; 247 | char* ptr=data; 248 | 249 | unsigned short len; 250 | picture.read((char*)&len, sizeof(len)); 251 | len = qFromBigEndian(len) - 2; // Two bytes for len are included in len value 252 | bool found=false; 253 | 254 | switch (marker) { 255 | 256 | case 0xe0: 257 | if (len >= 5) { 258 | for (int i=0; i<5; i++) 259 | picture.read(ptr++, sizeof(char)); 260 | if (strncmp(data, segmentTypes[0], 5) == 0) { 261 | readMetaDataJFIF(picture); 262 | return; 263 | } 264 | else if (strncmp(data, segmentTypes[1], 5) == 0) { 265 | readMetaDataJFXX(picture, len-5); 266 | return; 267 | } 268 | len -= 5; 269 | } 270 | break; 271 | 272 | case 0xe1: 273 | if (len >= 5) { 274 | for (int i=0; i<5; i++) 275 | picture.read(ptr++, sizeof(char)); 276 | if (strncmp(data, segmentTypes[2], 5) == 0 || strncmp(data, segmentTypes[3], 5) == 0) { 277 | //readMetaDataExif(picture, headerLength); // TODO 278 | // Parsing Exif data is in fact parsing a whole TIFF file... 279 | qDebug() << "EXIF not supported yet..."; 280 | found = true; 281 | 282 | } else if (len >= 28) { 283 | for (int i=0; i<23; i++) 284 | picture.read(ptr++, sizeof(char)); 285 | if (strncmp(data, segmentTypes[4], 23) == 0) { 286 | qDebug() << "Adobe XMP (eXtensible Metadata Platform) not supported yet..."; 287 | found = true; 288 | } 289 | len -= 23; 290 | } 291 | len -= 5; 292 | } 293 | break; 294 | 295 | case 0xe2: 296 | if (len >= 12) { 297 | for (int i=0; i<12; i++) 298 | picture.read(ptr++, sizeof(char)); 299 | if (strncmp(data, segmentTypes[5], 12) == 0) { 300 | qDebug() << "ICC profiles not supported..."; 301 | found = true; 302 | } 303 | len -= 12; 304 | } 305 | break; 306 | 307 | case 0xe3: 308 | if (len >= 5) { 309 | for (int i=0; i<5; i++) 310 | picture.read(ptr++, sizeof(char)); 311 | if (strncmp(data, segmentTypes[6], 5) == 0 || strncmp(data, segmentTypes[7], 5) == 0) { 312 | //readMetaDataExif(picture, headerLength); // TODO 313 | // Parsing Exif data is in fact parsing a whole TIFF file... 314 | qDebug() << "EXIF not supported yet..."; 315 | found = true; 316 | } 317 | len -= 5; 318 | } 319 | break; 320 | 321 | case 0xec: 322 | if (len >= 6) { 323 | for (int i=0; i<6; i++) 324 | picture.read(ptr++, sizeof(char)); 325 | if (strncmp(data, segmentTypes[8], 6) == 0) { 326 | // There is nothing in the "Ducky" tag that I can use, and my samples don't match description at 327 | // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/APP12.html#Ducky 328 | metadata["Photoshop 'Save for Web'"] = "yes"; 329 | len -= 6; 330 | found = true; 331 | 332 | } else { 333 | // Apparently APP12 (0xEC) is otherwise used as a comment 334 | char* text = new char[len]; 335 | char* tmp=text; 336 | for (int i=0; i= 14) { 348 | for (int i=0; i<14; i++) 349 | picture.read(ptr++, sizeof(char)); 350 | if (strncmp(data, segmentTypes[9], 14) == 0) { 351 | qDebug() << "Adobe Photoshop(R) metadata not supported..."; 352 | found = true; 353 | } 354 | len -= 14; 355 | } 356 | break; 357 | 358 | case 0xee: 359 | if (len >= 6) { 360 | for (int i=0; i<6; i++) 361 | picture.read(ptr++, sizeof(char)); 362 | if (strncmp(data, segmentTypes[10], 6) == 0) { 363 | // Adobe metadata - helps us detect CMYK and YCCK images 364 | unsigned char cc; 365 | for (int i=0; i<6; i++) 366 | picture.read ((char*)&cc, sizeof(cc)); 367 | if (cc == 0) 368 | adobeColorModel = Format::CMYK; // It can also be RGB if there are three components! 369 | else if (cc == 1) 370 | adobeColorModel = Format::YCBCR; 371 | else if (cc == 2) 372 | throw "YCCK color model currently not supported!"; 373 | return; 374 | } 375 | len -= 6; 376 | } 377 | break; 378 | 379 | } 380 | 381 | if (!found) 382 | qDebug() << "Unsupported Application segment, marker"<>4), (componentData[1] & 0x0F), componentData[2], fake); 471 | this->components.push_back(a); 472 | continue; 473 | } 474 | 475 | bool foundTable=false; 476 | for (unsigned int x=0;x>4), (componentData[1] & 0x0F), componentData[2], quantizationTables[x]); 480 | this->components.push_back(a); 481 | foundTable=true; 482 | 483 | } 484 | } 485 | 486 | if (!foundTable) { 487 | qDebug() << "Component "< maxHFactor) maxHFactor = components[i].HFactor; 495 | if (components[i].VFactor > maxVFactor) maxVFactor = components[i].VFactor; 496 | } 497 | for (uint i=0; iformat(); 508 | 509 | // Default JPEG color model 510 | f.setColorModel(Format::YCBCR); 511 | if (precision <= 8) 512 | f.setColorDepth(24); 513 | else 514 | // 12-bit is not directly supported by EtfShop, use 16-bit instead 515 | f.setColorDepth(48); 516 | 517 | if (numberOfComponents == 3) { 518 | // Note: Adobe nonstandard header uses code 0 to specify both CMYK and RGB 519 | if (adobeColorModel == Format::CMYK) { 520 | f.setColorModel(Format::RGB); 521 | metadata["Color model"] = "RGB"; 522 | } 523 | } 524 | 525 | else if (numberOfComponents == 4) { 526 | // Adobe proprietary extension 527 | if (adobeColorModel == Format::CMYK) { 528 | f.setColorModel(Format::CMYK); 529 | metadata["Color model"] = "CMYK"; 530 | 531 | if (precision <= 8) 532 | f.setColorDepth(32); 533 | else 534 | f.setColorDepth(64); 535 | f.setEncodingType(Format::COMPONENTWORD); // This is faster 536 | 537 | } else { 538 | qDebug() << "Specified 4 components, but no Adobe header!"; 539 | } 540 | } 541 | 542 | else if (numberOfComponents == 1) { 543 | f.setColorModel(Format::GRAYSCALE); 544 | if (precision <= 8) 545 | f.setColorDepth(8); 546 | else 547 | f.setColorDepth(16); 548 | // f.setEncodingType(Format::COMPONENTWORD); // This is faster 549 | } 550 | 551 | else { 552 | qDebug() << "Specified "<setFormat(f); 557 | 558 | imagePtr->setSize(pictureWidth, pictureHeight); 559 | imagePtr->activeLayer()->setSize(pictureWidth, pictureHeight); 560 | 561 | #if DEBUGLEVEL>0 562 | qDebug()<<"Image height is: "<>4; // Higher 4 bits of byte 588 | tableIdentifier = tableOptions & 0x0F; // Lower 4 bits of byte 589 | 590 | QuantizationTable t(false, false); 591 | t.tableID = tableIdentifier; 592 | 593 | // Elements are represented by 8 bits 594 | if (sizeOfElements == 0) { 595 | unsigned char el; 596 | for (int i=0; i<64; i++) { 597 | picture.read ((char*)&el, sizeof(el)); 598 | tableData[i] = el; 599 | } 600 | m=m+65; 601 | } 602 | // 16-bit elements 603 | else if (sizeOfElements==1) { 604 | unsigned short el; 605 | for (int i=0; i<64; i++) { 606 | picture.read ((char*)&el, sizeof(el)); 607 | el = qFromBigEndian(el); 608 | tableData[i] = el; 609 | } 610 | m=m+129; 611 | } 612 | 613 | inverseZigZagCoding (tableData, t.quantizationTableData); 614 | 615 | quantizationTables.push_back(t); 616 | 617 | #if DEBUGLEVEL>0 618 | qDebug() << "Read quantization table "<> 4; 643 | 644 | // Looking for tableID in tables 645 | HuffmanTable* table = 0; 646 | for (uint i=0; itableID == tableID && t->tableClass == tableClass) { 649 | table = huffmanTables[i]; 650 | break; 651 | } 652 | } 653 | 654 | // Not found, create a new table 655 | if (table == 0) { 656 | table = new HuffmanTable(); 657 | table->tableID = tableID; 658 | table->tableClass = tableClass; 659 | huffmanTables.push_back(table); 660 | } 661 | 662 | unsigned char codeLengths[16]; 663 | for(int i=0;i<16;i++) 664 | codeLengths[i]=0; 665 | 666 | unsigned long int code=0;//Huffman code which will be connected with element 667 | unsigned char element=0;//Read element from file 668 | 669 | // Read 16 bytes for number of elements which are coded by 1-16 bits 670 | picture.read ((char*)codeLengths, sizeof(codeLengths)); 671 | m+=16; 672 | 673 | for (int i=0; i<16; i++) { //We iterate through every element 674 | if (codeLengths[i]==0) { //If code length is 0, then we continue because no element is coded wiht "i" bits 675 | code *= 2; // When tree depth changes, we add additional bit, and shift code one place left 676 | continue; 677 | } 678 | for (int j=0; jcodes[element] = code; 683 | table->codeLengths[element] = i+1; 684 | code++; //Elements on the same tree depth have code incremented by one 685 | } 686 | 687 | code *= 2; //When tree depth changes, we add additional bit, and shift code one place left 688 | } 689 | #if DEBUGLEVEL>0 690 | qDebug() << "Read Huffman table ID: " << tableID << "class" << tableClass; 691 | #endif 692 | } 693 | } 694 | 695 | 696 | 697 | void JpegDecode::readArithmeticCoding(QFile &picture) { 698 | // Arithmetic coding not supported yet 699 | // Just skip data 700 | unsigned short len; 701 | unsigned char c; 702 | picture.read ((char*)&len, sizeof(len)); 703 | len = qFromBigEndian(len); 704 | for (int i=0; icomponents.size()) { 737 | qDebug() << "Number of components in SOS header is" << numberOfComponents << "but in SOF header is" << this->components.size(); 738 | qDebug() << "We will trust SOF header, but this probably wont work :("; 739 | numberOfComponents = this->components.size(); 740 | } 741 | 742 | if (numberOfComponents > ETF_FORMAT_MAX_COMPONENTS) { 743 | qDebug() << "Specified"<components[i].componentID != componentID) { 755 | qDebug() << "Component ID in SOS header is" << componentID << "but in SOF header is" << this->components[i].componentID; 756 | qDebug() << "We will trust SOF header"; 757 | } 758 | 759 | // Find AC and DC Huffman table in tables list 760 | unsigned char tableDC = tableID >> 4; 761 | unsigned char tableAC = tableID & 0x0F; 762 | componentTablesDC[i] = componentTablesAC[i] = 0; 763 | for (uint j=0; jtableID == tableDC && t->tableClass == 0) 766 | componentTablesDC[i] = t; 767 | if (t->tableID == tableAC && t->tableClass == 1) 768 | componentTablesAC[i] = t; 769 | } 770 | 771 | // Huffman tables not found 772 | if (componentTablesDC[i] == 0) { 773 | // Get first usable table 774 | for (uint j=0; jtableClass == 0) { 777 | componentTablesDC[i] = t; 778 | break; 779 | } 780 | } 781 | 782 | if (componentTablesDC[i] == 0) 783 | throw "File contains no DC Huffman tables!"; 784 | 785 | qDebug() << "SOS header specifies inexistant Huffman table" << tableDC << " - using " << componentTablesDC[i]->tableID; 786 | } 787 | 788 | if (componentTablesAC[i] == 0) { 789 | // Get first usable table 790 | for (uint j=0; jtableClass == 1) { 793 | componentTablesAC[i] = t; 794 | break; 795 | } 796 | } 797 | if (componentTablesAC[i] == 0) { 798 | // throw "File contains no AC Huffman tables!"; // Can happen in progressive JPEG! 799 | componentTablesAC[i] = componentTablesDC[i]; 800 | } 801 | 802 | qDebug() << "SOS header specifies inexistant Huffman table" << tableAC << " - using " << componentTablesAC[i]->tableID; 803 | } 804 | } 805 | 806 | // Start and end point for zig-zag coding 807 | picture.read ((char*)&zigZagStart, sizeof(zigZagStart)); 808 | picture.read ((char*)&zigZagEnd, sizeof(zigZagEnd)); 809 | 810 | // Bit approximation for progressive JPEG - TODO! 811 | unsigned char dummy; 812 | picture.read ((char*)&dummy, sizeof(dummy)); 813 | approximationH = dummy >> 4; 814 | approximationL = dummy & 0x0F; 815 | } 816 | 817 | 818 | 819 | void JpegDecode::readImageData(QFile &file) { 820 | uint currentComponent = 0; 821 | int counter2 = 1; // Counter for chroma subsampling 822 | int row[64], row2[8][8], row3[8][8]; 823 | int blkNo = 0; 824 | 825 | // Statistics of last DC value 826 | for (uint i=0; iwidth()]; 833 | } 834 | 835 | // Initialize data for addBlock method 836 | rawImagePointers[0] = (uchar*) imagePtr->activeLayer()->getData(); 837 | lineBytes = imagePtr->width() * components.size(); 838 | if (precision == 12) lineBytes *= 2; 839 | maxSample = pow(2, precision)-1; 840 | 841 | // scanline is aligned to the 32-bit boundary 842 | if (lineBytes % 4 != 0) 843 | lineBytes += 4 - lineBytes%4; 844 | 845 | for (uint i=0; i0 && v>0) 863 | tmp /= 4; 864 | else 865 | tmp *= 0.1767766952966368811; 866 | coefficients[index] = tmp; 867 | } 868 | } 869 | } 870 | } 871 | 872 | 873 | 874 | 875 | // Benchmarking 876 | 877 | clock_t t1, t2, t3, t4, t5, t6; 878 | huffmanTime = zigZagTime = quantizeTime = idctTime = addBlockTime = 0; 879 | 880 | #if DEBUGLEVEL>0 881 | qDebug()<<"Start of scan"; 882 | #endif 883 | 884 | endOfFile = false; 885 | while (!endOfFile) { 886 | #if DEBUGLEVEL>1 887 | qDebug()<<"--- Huffman read block "<components[currentComponent].HFactor * this->components[currentComponent].VFactor) 922 | counter2++; 923 | else { 924 | counter2 = 1; 925 | currentComponent++; 926 | if (currentComponent == components.size()) currentComponent=0; 927 | } 928 | blkNo++; 929 | 930 | // Benchmarking 931 | #ifdef BENCHMARK 932 | huffmanTime += (t2-t1) / (CLOCKS_PER_SEC / 1000); 933 | zigZagTime += (t3-t2) / (CLOCKS_PER_SEC / 1000); 934 | quantizeTime += (t4-t3) / (CLOCKS_PER_SEC / 1000); 935 | idctTime += (t5-t4) / (CLOCKS_PER_SEC / 1000); 936 | addBlockTime += (t6-t5) / (CLOCKS_PER_SEC / 1000); 937 | if (blkNo % 10000 == 0) { 938 | qDebug() << "Huffmannn "<0 950 | qDebug()<<"End of scan"; 951 | #endif 952 | 953 | } 954 | 955 | 956 | 957 | bool JpegDecode::readMoreData(QFile &picture, unsigned int &data, unsigned int ¤tDataLength) { 958 | unsigned char binaryData; 959 | 960 | // Detect errors 961 | if (currentDataLength > 24) { // Unsigned int can hold at most 32 = 24+8 bits 962 | cout << "ERROR: Code value not found in Huffman table: "<> (currentDataLength-1)) << (currentDataLength-1)); 966 | currentDataLength--; 967 | return true; 968 | } 969 | 970 | if (picture.read((char*)&binaryData, sizeof binaryData) == 0) 971 | return false; // End of file 972 | 973 | // We read byte and put it in low 8 bits of variable data 974 | if (binaryData == 0xFF) { 975 | data = (data << 8) + binaryData; 976 | currentDataLength += 8; // Increase current data length for 8 because we read one new byte 977 | if (picture.read((char*)&binaryData, sizeof binaryData) == 0) 978 | return false; 979 | 980 | // End of Image marker 981 | if (binaryData == 0xd9) { 982 | // Drop 0xFF from data 983 | data = data >> 8; 984 | currentDataLength -= 8; 985 | #if DEBUGLEVEL>1 986 | cout << "End of image marker"<= 0xd0 && binaryData <= 0xd7) { 993 | #if DEBUGLEVEL>1 994 | cout << "Restart marker"<1 1008 | cout << "Stuffing"<1 1049 | cout << "Byte "<0 1051 | if (byteno % 1000 == 0) 1052 | cout << "Byte "<codes[i] == 0xFFFFFFFF) 1063 | continue; 1064 | 1065 | // If current data length is greater or equal than n, compare first n bits (n - length of current Huffman code) 1066 | uint n = htable->codeLengths[i]; 1067 | 1068 | if (currentDataLength >= n && htable->codes[i] == data >> (currentDataLength-n)) { 1069 | #if DEBUGLEVEL>1 1070 | cout << "Found data "<codes[i]<codes[i] << currentDataLength); 1076 | 1077 | // Reading of DC coefficients 1078 | if (ACDC == DC) { 1079 | unsigned char bitLength = i; // Next i bits represent DC coefficient value 1080 | 1081 | // Do we need to read more bits of data? 1082 | while (currentDataLength0 1086 | qDebug() << "End of file encountered inside a Huffman code!"; 1087 | #endif 1088 | break; 1089 | } 1090 | byteno++; 1091 | #if DEBUGLEVEL>1 1092 | cout << "Byte(+) "<> (currentDataLength-bitLength); 1098 | currentDataLength -= bitLength; 1099 | data = data - (DCCoeficient<>(bitLength-1)) == 0 ) { 1104 | DCCoeficient = DCCoeficient - (2 << (bitLength-1)) + 1; 1105 | } 1106 | //cout << "After substract "<1 1110 | cout << "DC READ "<= ACcount+1) { 1144 | #if DEBUGLEVEL>0 1145 | qDebug() << "Huffman error: 16 AC zeros requested, but only "<> 4; 1159 | unsigned char Sbits = ACElement & 0x0F; 1160 | 1161 | // Before our element there is Rbits zero elements 1162 | for (int k=0; k= ACcount) { 1164 | #if DEBUGLEVEL>0 1165 | qDebug() << "Huffman error: "<0 1179 | qDebug() << "End of file encountered inside a Huffman code!"; 1180 | #endif 1181 | break; 1182 | } 1183 | byteno++; 1184 | #if DEBUGLEVEL>1 1185 | cout << "Byte(+) "<> (currentDataLength-Sbits); 1191 | currentDataLength -= Sbits; 1192 | data = data - (ACCoeficient<>(Sbits-1)) == 0 ) { 1196 | ACCoeficient = ACCoeficient - (2 << (Sbits-1)) + 1; 1197 | } 1198 | dataBlock[m] = ACCoeficient; 1199 | m++; 1200 | } 1201 | 1202 | // End of block 1203 | if (m >= ACcount+1) 1204 | return; 1205 | 1206 | if (currentDataLength<3) // If currentData length is < 3, we need to read new byte, so leave this for loop 1207 | break; 1208 | i =- 1; // currentDataLength is not zero, set i=0 to start from first element of array 1209 | } 1210 | 1211 | } 1212 | } 1213 | } while(readMoreData(picture, data, currentDataLength)); 1214 | 1215 | endOfFile = true; // We reached an end 1216 | } 1217 | 1218 | 1219 | void JpegDecode::multiplyWithQuantizationTable(int dataBlock[8][8], int currentComponent) { 1220 | QuantizationTable* table = this->components[currentComponent].componentQuantizationTable; 1221 | 1222 | for(int i=0;i<8;i++){ 1223 | for(int j=0;j<8;j++){ 1224 | dataBlock[i][j] = dataBlock[i][j] * table->quantizationTableData[i][j]; 1225 | } 1226 | } 1227 | } 1228 | 1229 | 1230 | /* This method will be called repeatedly to place 8x8 blocks on the picture, from 1231 | left to right and then onto next line. 1232 | */ 1233 | 1234 | void JpegDecode::addBlock(int dataBlock[8][8], int comp /* Current component */ ) { 1235 | 1236 | // Avoid function calls... these functions are inline but the library is external... 1237 | int width = imagePtr->width(), height = imagePtr->height(); 1238 | int pixelJump = components.size(); 1239 | if (precision == 12) pixelJump *= 2; 1240 | 1241 | int y; 1242 | for (y=0; y<8; y++) { 1243 | int x; 1244 | for (x=0; x<8 && currentX[comp]maxSample) value=maxSample; 1249 | 1250 | // Hack: Adobe JPEG CMYK format is actually inverted 1251 | if (imagePtr->format().getColorModel() == Format::CMYK) 1252 | value=maxSample-value; 1253 | 1254 | if (precision == 8) { 1255 | *rawImagePointers[comp] = value; 1256 | rawImagePointers[comp] += pixelJump; 1257 | 1258 | } else { 1259 | // ETFShop doesn't support 12-bit precision so we multiply sample by 16 1260 | // to get 16-bit precision value 1261 | *rawImagePointers[comp]++ = (value >> 4); 1262 | *rawImagePointers[comp] = (value << 4) & 0x00FF; 1263 | rawImagePointers[comp] += pixelJump - 1; 1264 | } 1265 | } 1266 | 1267 | // Move currentX and rawImagePointer to where they were before x loop 1268 | currentX[comp] -= x; 1269 | rawImagePointers[comp] -= pixelJump*x; 1270 | 1271 | // Increment currentY to next line 1272 | if (currentY[comp] >= height-1) 1273 | break; 1274 | currentY[comp]++; 1275 | 1276 | // Move rawImagePointer to next row (one scanline below) 1277 | rawImagePointers[comp] += lineBytes; 1278 | } 1279 | 1280 | // currentX and currentY are now 8 pixels below values when function is called 1281 | // they should be 8 pixels to the right 1282 | // unless this is image edge, in which case they should be in the next line 1283 | currentX[comp] += 8; 1284 | // Move pointer along 1285 | rawImagePointers[comp] += pixelJump*8; 1286 | 1287 | if (currentX[comp] >= width) { 1288 | if (currentY[comp] >= height && comp == components.size()-1) 1289 | endOfFile = true; // We've reached the end of image 1290 | 1291 | rawImagePointers[comp] -= pixelJump*currentX[comp]; 1292 | currentX[comp] = 0; 1293 | 1294 | } else { 1295 | // If this is one of the bottom blocks, y may be less than 8 1296 | currentY[comp] -= y; 1297 | rawImagePointers[comp] -= y*lineBytes; 1298 | } 1299 | } 1300 | 1301 | 1302 | /* This method is much more complex, having to deal with subsampling. 1303 | Also somewhat slower due to all the counters. */ 1304 | 1305 | 1306 | void JpegDecode::addBlockSubsampling(int dataBlock[8][8], int comp /* Current component */ ) { 1307 | // cout << "Adding block at "<width(), height = imagePtr->height(); 1311 | int pixelJump = components.size(); 1312 | if (precision == 12) pixelJump *= 2; 1313 | 1314 | int HFactor = components[comp].HFactor, VFactor = components[comp].VFactor; 1315 | int HScale = components[comp].HScale, VScale = components[comp].VScale; 1316 | 1317 | // Iterate through layer in imagePtr and put pixels 1318 | LayerPtr layer = imagePtr->activeLayer(); 1319 | for (uint y=0; y<8; y++) { 1320 | if (currentX[comp] >= width) break; 1321 | if (currentY[comp] + y >= height) break; 1322 | 1323 | // Repeat each line VScale times 1324 | for (int vfy=0; vfy= height) break; 1327 | 1328 | Pixel p = layer->getPixelAt(currentX[comp], imageY); 1329 | for (uint x=0; x<8; x++) { 1330 | if (currentX[comp] + x * HScale >= width) break; 1331 | 1332 | // Round value - x and y are reverted compared to image 1333 | int value = dataBlock[y][x]; 1334 | if (value<0) value=0; 1335 | if (value>maxSample) value=maxSample; 1336 | 1337 | // Hack: Adobe JPEG CMYK format is actually inverted 1338 | if (imagePtr->format().getColorModel() == Format::CMYK) 1339 | value=maxSample-value; 1340 | 1341 | // ETFShop doesn't support 12-bit precision so we're using 16-bit 1342 | if (precision == 12) 1343 | value = value << 4; 1344 | 1345 | // Repeat each pixel HScale times 1346 | int realx = currentX[comp] + x * HScale; 1347 | for (int i=0; i= width) break; 1351 | } 1352 | } 1353 | } 1354 | } 1355 | 1356 | 1357 | // Update starting X and Y for next block, taking into account subsampling 1358 | currentX[comp] += 8 * HScale; 1359 | currentBlockHFactor[comp]++; 1360 | if (currentBlockHFactor[comp] >= HFactor) { 1361 | currentX[comp] -= 8 * HScale * HFactor; 1362 | currentBlockHFactor[comp] = 0; 1363 | 1364 | currentY[comp] += 8 * VScale; 1365 | currentBlockVFactor[comp]++; 1366 | 1367 | if (currentBlockVFactor[comp] >= VFactor) { 1368 | currentY[comp] -= 8 * VScale * VFactor; 1369 | currentBlockVFactor[comp] = 0; 1370 | 1371 | currentX[comp] += 8 * HScale * HFactor; 1372 | if (currentX[comp] >= width) { 1373 | currentX[comp] = 0; 1374 | currentY[comp] += 8 * VScale * VFactor; 1375 | 1376 | // Force end here because Progressive JPEG file has more stuff after this 1377 | if (currentY[comp] >= height && comp == components.size()-1) 1378 | endOfFile = true; 1379 | } 1380 | } 1381 | } 1382 | } 1383 | 1384 | 1385 | void JpegDecode::inverseZigZagCoding(char *array, unsigned char** matrix){ 1386 | //k- is array index, i,j are index of matrix 1387 | matrix[0][0]=array[0];//Take the first element 1388 | int k=1;//Inex of array set to second element 1389 | int i=0,j=1;//Define index for matrix 1390 | while(1){ 1391 | while(j!=0 && i!=7){//Going upside down until j!=0 1392 | matrix[i][j]=array[k]; 1393 | i=i+1; 1394 | j=j-1; 1395 | k=k+1; 1396 | } 1397 | matrix[i][j]=array[k];//Take the edge element 1398 | k=k+1;//Increment array index 1399 | 1400 | if(i<7)//If not last row, increment i 1401 | i=i+1; 1402 | 1403 | else if(i==7)//If we hit the last row, we go right one place 1404 | j=j+1; 1405 | 1406 | 1407 | while(i!=0 && j!=7){//Going bottom up 1408 | matrix[i][j]=array[k]; 1409 | i=i-1; 1410 | j=j+1; 1411 | k=k+1; 1412 | } 1413 | matrix[i][j]=array[k];//Take edge element 1414 | k=k+1;//Increment array index 1415 | if(j<7)//If we didn't hit the edge, increment j 1416 | j=j+1; 1417 | 1418 | else if(j==7)//If we hit the last element, go down one place 1419 | i=i+1; 1420 | 1421 | if(i>=7 && j>=7)//If we hit last element matrix[8][8] exit 1422 | break; 1423 | } 1424 | } 1425 | 1426 | void JpegDecode::inverseZigZagCoding(int *array, int matrix[8][8] ) { 1427 | int matrixX[64] = {0, 1, 0, 0, 1, 2, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 5, 6, 7, 7, 6, 7}; 1428 | int matrixY[64] = {0, 0, 1, 2, 1, 0, 0, 1, 2, 3, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 4, 5, 6, 7, 7, 6, 5, 6, 7, 7}; 1429 | 1430 | 1431 | for (int i=zigZagStart; i<=zigZagEnd; i++) 1432 | // (*matrix)[matrixY[i]][matrixX[i]] = array[i-zigZagStart]; 1433 | matrix[matrixY[i]][matrixX[i]] = array[i-zigZagStart]; 1434 | 1435 | /* //k- is array index, i,j are index of matrix 1436 | (*matrix)[0][0]=array[0];//Take the first element 1437 | unsigned int k=1;//Inex of array set to second element 1438 | unsigned int i=0,j=1;//Define index for matrix 1439 | while(1){ 1440 | while(j!=0 && i!=7){//Going upside down until j!=0 1441 | (*matrix)[i][j]=array[k]; 1442 | i=i+1; 1443 | j=j-1; 1444 | k=k+1; 1445 | } 1446 | (*matrix)[i][j]=array[k];//Take the edge element 1447 | k=k+1;//Increment array index 1448 | 1449 | if(i<7)//If not last row, increment i 1450 | i=i+1; 1451 | 1452 | else if(i==7)//If we hit the last row, we go right one place 1453 | j=j+1; 1454 | 1455 | 1456 | while(i!=0 && j!=7){//Going bottom up 1457 | (*matrix)[i][j]=array[k]; 1458 | i=i-1; 1459 | j=j+1; 1460 | k=k+1; 1461 | } 1462 | (*matrix)[i][j]=array[k];//Take edge element 1463 | k=k+1;//Increment array index 1464 | if(j<7)//If we didn't hit the edge, increment j 1465 | j=j+1; 1466 | 1467 | else if(j==7)//If we hit the last element, go down one place 1468 | i=i+1; 1469 | 1470 | if(i>=7 && j>=7)//If we hit last element matrix[8][8] exit 1471 | break; 1472 | }*/ 1473 | } 1474 | 1475 | /*void JpegDecode::inverseZigZagCoding(char *array, vector > &matrix ){ 1476 | 1477 | //k- is array index, i,j are index of matrix 1478 | matrix[0][0]=array[0];//Take the first element 1479 | int k=1;//Inex of array set to second element 1480 | int i=0,j=1;//Define index for matrix 1481 | while(1){ 1482 | while(j!=0 && i!=7){//Going upside down until j!=0 1483 | matrix[i][j]=array[k]; 1484 | i=i+1; 1485 | j=j-1; 1486 | k=k+1; 1487 | } 1488 | matrix[i][j]=array[k];//Take the edge element 1489 | k=k+1;//Increment array index 1490 | 1491 | if(i<7)//If not last row, increment i 1492 | i=i+1; 1493 | 1494 | else if(i==7)//If we hit the last row, we go right one place 1495 | j=j+1; 1496 | 1497 | 1498 | while(i!=0 && j!=7){//Going bottom up 1499 | matrix[i][j]=array[k]; 1500 | i=i-1; 1501 | j=j+1; 1502 | k=k+1; 1503 | } 1504 | matrix[i][j]=array[k];//Take edge element 1505 | k=k+1;//Increment array index 1506 | if(j<7)//If we didn't hit the edge, increment j 1507 | j=j+1; 1508 | 1509 | else if(j==7)//If we hit the last element, go down one place 1510 | i=i+1; 1511 | 1512 | if(i>=7 && j>=7)//If we hit last element matrix[8][8] exit 1513 | break; 1514 | } 1515 | }*/ 1516 | 1517 | void JpegDecode::IDCT (int block[8][8], int transformedBlock[8][8]) { 1518 | int sum=0; 1519 | int counter=0; 1520 | 1521 | for(int i=0;i<8;i++){ 1522 | for(int j=0;j<8;j++){ 1523 | sum=0; 1524 | for(int u=0;u<8;u++){ 1525 | for(int v=0;v<8;v++){ 1526 | sum = sum + block[u][v] * coefficients[ counter++ ]; 1527 | } 1528 | } 1529 | 1530 | // All coefficients are multiplied by 1024 since they are int 1531 | sum = sum >> 10; 1532 | 1533 | // Only 8 and 12-bit is supported by DCT per JPEG standard 1534 | if (precision == 8) 1535 | transformedBlock[i][j]=sum+128; 1536 | else if (precision == 12) 1537 | transformedBlock[i][j]=sum+2048; 1538 | } 1539 | } 1540 | } 1541 | 1542 | 1543 | void JpegDecode::addLossless(int *array, int currentComponent) { 1544 | // Different meanings for SOF values 1545 | unsigned char predictor = zigZagStart; 1546 | unsigned char pointTransform = approximationL; 1547 | 1548 | static unsigned int currentX[ETF_FORMAT_MAX_COMPONENTS] = {0}; 1549 | static unsigned int currentY[ETF_FORMAT_MAX_COMPONENTS] = {0}; 1550 | static Pixel* pixels[ETF_FORMAT_MAX_COMPONENTS] = {0}; 1551 | if (pixels[0] == 0) { 1552 | for (int i=0; iactiveLayer()->getPixelAt(0,0)); 1554 | } 1555 | 1556 | // Predictors 1557 | int* slc = scanLineCache[currentComponent]; 1558 | int Ra=array[0], Rb=slc[currentX[currentComponent]+1], Rc=slc[currentX[currentComponent]]; 1559 | if (currentX[currentComponent] == 0 && currentY[currentComponent] == 0) 1560 | previousDC[currentComponent] = pow(2, precision - pointTransform - 1); 1561 | else if (predictor == 0) 1562 | previousDC[currentComponent] = 0; 1563 | else if (currentX[currentComponent] == imagePtr->width() - 1) 1564 | previousDC[currentComponent] = slc[0]; 1565 | else if (predictor == 1 || currentY[currentComponent] == 0) 1566 | previousDC[currentComponent] = Ra; 1567 | else if (predictor == 2) 1568 | previousDC[currentComponent] = Rb; 1569 | else if (predictor == 3) 1570 | previousDC[currentComponent] = Rc; 1571 | else if (predictor == 4) 1572 | previousDC[currentComponent] = Ra + Rb - Rc; 1573 | else if (predictor == 5) 1574 | previousDC[currentComponent] = Ra + ((Rb - Rc) >> 1); 1575 | else if (predictor == 6) 1576 | previousDC[currentComponent] = Rb + ((Ra - Rc) >> 1); 1577 | else if (predictor == 7) 1578 | previousDC[currentComponent] = (Ra + Rb) >> 1; 1579 | 1580 | slc[currentX[currentComponent]] = array[0]; 1581 | 1582 | 1583 | // we are using either 8-bit or 16-bit precision so we must increase value 1584 | if (precision > 8) 1585 | array[0] = array[0] << (16-precision); 1586 | else 1587 | array[0] = array[0] << (8-precision); 1588 | 1589 | pixels[currentComponent]->setComponent(currentComponent, array[0]); 1590 | pixels[currentComponent]->next(); 1591 | 1592 | currentX[currentComponent] ++; 1593 | if (currentX[currentComponent] >= imagePtr->width()) { 1594 | currentX[currentComponent] -= imagePtr->width(); 1595 | currentY[currentComponent]++; 1596 | } 1597 | if (currentY[currentComponent] >= imagePtr->height() && currentComponent == components.size()-1) 1598 | endOfFile = true; 1599 | } 1600 | 1601 | 1602 | 1603 | 1604 | 1605 | 1606 | 1607 | 1608 | // ---------- VARIOUS DEBUGGING METHODS 1609 | 1610 | string JpegDecode::binary (unsigned int v) { 1611 | char binstr[17] ; 1612 | int i ; 1613 | 1614 | binstr[16] = '\0' ; 1615 | for (i=0; i<16; i++) { 1616 | binstr[15-i] = v & 1 ? '1' : '0' ; 1617 | v = v / 2 ; 1618 | } 1619 | 1620 | return binstr ; 1621 | string a=binstr; 1622 | return a; 1623 | } 1624 | 1625 | 1626 | void JpegDecode::outputQuantizationTables(){ 1627 | for(uint x=0;xcomponents.size();x++){ 1628 | for(int i=0;i<8;i++){ 1629 | for(int j=0;j<8;j++) 1630 | std::cout<<(int)this->components[x].componentQuantizationTable->quantizationTableData[i][j]<<" "; 1631 | std::cout<tableID<<"Class"<tableClass; 1644 | for (uint j=0; jcodes.size(); j++) { 1645 | if (table->codes[j] == 0xFFFFFFFF) 1646 | continue; 1647 | qDebug() << "Code no"<codes[j]).c_str()<<"Length"<codeLengths[j]; 1648 | } 1649 | } 1650 | 1651 | qDebug() << ""; 1652 | 1653 | } 1654 | 1655 | 1656 | void JpegDecode::dumpMetaData() { 1657 | qDebug() << "METADATA:"; 1658 | QMapIterator i(metadata); 1659 | while (i.hasNext()) { 1660 | i.next(); 1661 | qDebug() << i.key() << ": " << i.value(); 1662 | } 1663 | } 1664 | 1665 | 1666 | void JpegDecode::dumpBlock(int block[8][8]) { 1667 | qDebug() << "Dump block:"; 1668 | for (int i=0; i<8; i++) { 1669 | for (int j=0; j<8; j++) 1670 | cout << setw(5) << block[i][j] << " "; 1671 | cout < 9 | #include 10 | #include 11 | #include 12 | #include "component.h" 13 | #include "huffmantable.h" 14 | #include 15 | 16 | #include "imagestate.h" 17 | 18 | class JpegDecode { 19 | 20 | public: 21 | 22 | JpegDecode(QFile &jpegFile); 23 | ImageStatePtr imagePtr; 24 | QImage getImage(){ 25 | return imagePtr->mergedQImage(); 26 | } 27 | 28 | private: 29 | 30 | // Various metadata and picture information 31 | QMap metadata; 32 | vector thumbnail; 33 | unsigned char precision; // Precison of elements (8 or 12 bits) 34 | 35 | // Data needed for decoding 36 | vector components; 37 | vector quantizationTables; 38 | vector huffmanTables; 39 | HuffmanTable* componentTablesDC[ETF_FORMAT_MAX_COMPONENTS]; // from format.h 40 | HuffmanTable* componentTablesAC[ETF_FORMAT_MAX_COMPONENTS]; 41 | bool endOfFile; 42 | int previousDC[ETF_FORMAT_MAX_COMPONENTS]; 43 | Format::ColorModel adobeColorModel; 44 | bool losslessFormat; 45 | unsigned char zigZagStart, zigZagEnd; 46 | unsigned char approximationH, approximationL; 47 | int *scanLineCache[ETF_FORMAT_MAX_COMPONENTS]; 48 | 49 | // Data used by addBlock method 50 | uchar* rawImagePointers[ETF_FORMAT_MAX_COMPONENTS]; 51 | int lineBytes; 52 | unsigned int currentX[ETF_FORMAT_MAX_COMPONENTS]; // Coordinates for next block 53 | unsigned int currentY[ETF_FORMAT_MAX_COMPONENTS]; 54 | int maxSample; 55 | bool hasSubSampling; 56 | int currentBlockHFactor[ETF_FORMAT_MAX_COMPONENTS], currentBlockVFactor[ETF_FORMAT_MAX_COMPONENTS]; 57 | 58 | // Data used by IDCT method 59 | double cosine[106]; 60 | int coefficients[4096]; 61 | 62 | // Main loop 63 | void readFile(QFile &file); 64 | 65 | // Loop steps 66 | void readAppSegment(QFile &picture, unsigned char marker); 67 | void readMetaDataJFIF(QFile &picture); 68 | void readMetaDataJFXX(QFile &picture, int headerLength); 69 | 70 | void readFrameHeader(QFile &picture, unsigned char marker); 71 | void readHuffmanTables(QFile &picture); 72 | void readArithmeticCoding(QFile &picture); 73 | void readQuantizationTables(QFile &picture); 74 | void readComments(QFile &picture); 75 | void readScanHeader(QFile &picture); 76 | void readImageData(QFile &picture); 77 | 78 | //HuffmanTable huffmanTable; 79 | void readHuffmanBlock(QFile &picture, int* block, int currentComponent); 80 | bool readMoreData(QFile &picture, unsigned int &data, unsigned int ¤tDataLength); 81 | 82 | bool isEndOfFile() { return endOfFile; } 83 | 84 | void IDCT (int block8x8[8][8], int block2[8][8]); 85 | void multiplyWithQuantizationTable(int dataBlock[8][8], int currentComponent); 86 | void addBlock(int dataBlock[8][8], int currentComponent); 87 | void addBlockSubsampling(int dataBlock[8][8], int currentComponent); 88 | void addLossless(int *array, int currentComponent); 89 | 90 | void inverseZigZagCoding(char *array,unsigned char ** matrix); 91 | void inverseZigZagCoding(int *array, int matrix[8][8]); 92 | 93 | // Debug 94 | string binary (unsigned int v); 95 | void outputQuantizationTables(); 96 | void outputHuffmanCodes(); 97 | void dumpMetaData(); 98 | void dumpBlock(int block[8][8]); 99 | 100 | // Benchmark 101 | unsigned int huffmanTime, zigZagTime, quantizeTime, idctTime, addBlockTime; 102 | 103 | }; 104 | 105 | 106 | #endif // JPEGDECODE_H 107 | -------------------------------------------------------------------------------- /jpegencode.cpp: -------------------------------------------------------------------------------- 1 | #include "jpegencode.h" 2 | #include 3 | #include 4 | #include 5 | 6 | const double pi=3.1415926535897932384626433832795; 7 | 8 | void JpegEncode::transformToYCbCr(){ 9 | int R,G,B; 10 | for(unsigned int i=0;i > &image, vector &zigZagArray, int quantizationTable[8][8]){ 30 | vector >block8x8(8,vector(8)); 31 | double sum=0; 32 | double previousDCCoefficient=0,help=0;//In this function DCPM is performed so DC coeficient is generated with formula Diff=DCi-DCi-1 33 | 34 | //Iterating through image 35 | for(unsigned int x=0;x0 && v==0)||(u==0 && v>0)) 44 | block8x8[u][v]=0.176776695296636881; 45 | else if(u>0 && v>0) 46 | block8x8[u][v]=0.25; 47 | sum=0; 48 | //counting sum 49 | for(int i=0;i<8;i++){ 50 | for(int j=0;j<8;j++){ 51 | sum=sum+(cos(((2*i+1)*u*pi)/16)*cos(((2*j+1)*v*pi)/16)*image[x+i][y+j]); 52 | 53 | } 54 | } 55 | if(u==0 && v==0){//DPCM for DC element of component 56 | block8x8[u][v]=((block8x8[u][v]*sum))/quantizationTable[u][v]; 57 | help=block8x8[u][v]; 58 | block8x8[u][v]=(block8x8[u][v]-previousDCCoefficient); 59 | } 60 | else 61 | block8x8[u][v]=(block8x8[u][v]*sum)/quantizationTable[u][v]; 62 | 63 | 64 | } 65 | } 66 | //Do ZigZag coding, and array is ready for Huffman coding 67 | ZigZagCoding(block8x8,zigZagArray); 68 | previousDCCoefficient=help; 69 | } 70 | } 71 | } 72 | 73 | char JpegEncode::getCategoryOfDCTCoefficient(int x){ 74 | if(x==0) 75 | return 0; 76 | else if(x==-1 || x==1) 77 | return 1; 78 | else if((x>=-3 && x<=-2)||(x>=2 && x<=3)) 79 | return 2; 80 | else if((x>=-7 && x<=-4)||(x>=4 && x<=7)) 81 | return 3; 82 | else if((x>=-15 && x<=-8)||(x>=8 && x<=15)) 83 | return 4; 84 | else if((x>=-31 && x<=-16)||(x>=16 && x<=31)) 85 | return 5; 86 | else if((x>=-63 && x<=-32)||(x>=32 && x<=63)) 87 | return 6; 88 | else if((x>=-127 && x<=-64)||(x>=64 && x<=127)) 89 | return 7; 90 | else if((x>=-255 && x<=-128)||(x>=128 && x<=255)) 91 | return 8; 92 | else if((x>=-511 && x<=-256)||(x>=256 && x<=511)) 93 | return 9; 94 | else if((x>=-1023 && x<=-512)||(x>=512 && x<=1023)) 95 | return 10; 96 | else if((x>=-2047 && x<=-1024)||(x>=1024 && x<=2047)) 97 | return 11; 98 | else if((x>=-4095 && x<=-2048)||(x>=2048 && x<=4095)) 99 | return 12; 100 | else if((x>=-8191 && x<=-4096)||(x>=4096 && x<=8191)) 101 | return 13; 102 | else if((x>=-16383 && x<=-8192)||(x>=8192 && x<=16383)) 103 | return 14; 104 | else if((x>=-32767 && x<=-16384)||(x>=16384 && x<=32767)) 105 | return 15; 106 | else 107 | return 0; 108 | } 109 | 110 | 111 | void JpegEncode::ZigZagCoding(vector > &block8x8,vector&zigZagArray){ 112 | //k- is zigZagArray index, i,j are index of matrix 113 | zigZagArray.push_back(block8x8[0][0]);//Take the first element 114 | int i=0,j=1;//Define index for matrix 115 | while(1){ 116 | while(j!=0 && i!=7){//Going upside down until j!=0 117 | zigZagArray.push_back(block8x8[i][j]); 118 | i=i+1; 119 | j=j-1; 120 | } 121 | zigZagArray.push_back(block8x8[i][j]);//Take the edge element 122 | 123 | if(i<7)//If not last row, increment i 124 | i=i+1; 125 | 126 | else if(i==7)//If we hit the last row, we go right one place 127 | j=j+1; 128 | 129 | 130 | while(i!=0 && j!=7){//Going bottom up 131 | zigZagArray.push_back(block8x8[i][j]); 132 | i=i-1; 133 | j=j+1; 134 | } 135 | zigZagArray.push_back(block8x8[i][j]);//Take edge element 136 | if(j<7)//If we didn't hit the edge, increment j 137 | j=j+1; 138 | 139 | else if(j==7)//If we hit the last element, go down one place 140 | i=i+1; 141 | 142 | if(i>=7 && j>=7)//If we hit last element matrix[8][8] exit 143 | break; 144 | } 145 | } 146 | void JpegEncode::ZigZagCoding(int block8x8[8][8],vector&zigZagArray){ 147 | //k- is zigZagArray index, i,j are index of matrix 148 | zigZagArray.push_back(block8x8[0][0]);//Take the first element 149 | int i=0,j=1;//Define index for matrix 150 | while(1){ 151 | while(j!=0 && i!=7){//Going upside down until j!=0 152 | zigZagArray.push_back(block8x8[i][j]); 153 | i=i+1; 154 | j=j-1; 155 | } 156 | zigZagArray.push_back(block8x8[i][j]);//Take the edge element 157 | 158 | if(i<7)//If not last row, increment i 159 | i=i+1; 160 | 161 | else if(i==7)//If we hit the last row, we go right one place 162 | j=j+1; 163 | 164 | 165 | while(i!=0 && j!=7){//Going bottom up 166 | zigZagArray.push_back(block8x8[i][j]); 167 | i=i-1; 168 | j=j+1; 169 | } 170 | zigZagArray.push_back(block8x8[i][j]);//Take edge element 171 | if(j<7)//If we didn't hit the edge, increment j 172 | j=j+1; 173 | 174 | else if(j==7)//If we hit the last element, go down one place 175 | i=i+1; 176 | 177 | if(i>=7 && j>=7)//If we hit last element matrix[8][8] exit 178 | break; 179 | } 180 | } 181 | void JpegEncode::ZigZagCoding(vector > &block8x8,vector&zigZagArray){ 182 | 183 | //k- is zigZagArray index, i,j are index of matrix 184 | zigZagArray.push_back(static_cast(block8x8[0][0]));//Take the first element 185 | int i=0,j=1;//Define index for matrix 186 | while(1){ 187 | while(j!=0 && i!=7){//Going upside down until j!=0 188 | zigZagArray.push_back(static_cast(block8x8[i][j])); 189 | i=i+1; 190 | j=j-1; 191 | } 192 | zigZagArray.push_back(static_cast(block8x8[i][j]));//Take the edge element 193 | 194 | if(i<7)//If not last row, increment i 195 | i=i+1; 196 | 197 | else if(i==7)//If we hit the last row, we go right one place 198 | j=j+1; 199 | 200 | 201 | while(i!=0 && j!=7){//Going bottom up 202 | zigZagArray.push_back(static_cast(block8x8[i][j])); 203 | i=i-1; 204 | j=j+1; 205 | } 206 | zigZagArray.push_back(static_cast(block8x8[i][j]));//Take edge element 207 | if(j<7)//If we didn't hit the edge, increment j 208 | j=j+1; 209 | 210 | else if(j==7)//If we hit the last element, go down one place 211 | i=i+1; 212 | 213 | if(i>=7 && j>=7)//If we hit last element matrix[8][8] exit 214 | break; 215 | } 216 | } 217 | JpegEncode::JpegEncode(QImage &exportImage,const QString& filename) : QuantizationTableWriteProcessLuminance(true,true), QuantizationTableWriteProcessChrominance(true,false), imageToExport(exportImage,filename) { 218 | 219 | 220 | } 221 | 222 | 223 | void JpegEncode::generateCodeSizes(vector&freq,vector&codeSize,vector&others){ 224 | int v1=0,v2=0; 225 | while(1){ 226 | v1=findV(freq,-1); 227 | v2=findV(freq,v1); 228 | if(v2==-1) 229 | return; 230 | freq[v1]=freq[v1]+freq[v2]; 231 | freq[v2]=0; 232 | 233 | codeSize[v1]++; 234 | 235 | while(others[v1]!=-1){ 236 | v1=others[v1]; 237 | codeSize[v1]++; 238 | } 239 | 240 | others[v1]=v2; 241 | codeSize[v2]++; 242 | 243 | while(others[v2]!=-1){ 244 | v2=others[v2]; 245 | codeSize[v2]++; 246 | } 247 | } 248 | } 249 | 250 | void JpegEncode::generateBytesAfterBITS(vector&codeSize,vector&huffmanValue){ 251 | int j=0,i=1; 252 | while(i<=32){ 253 | j=0; 254 | while(j<=255){ 255 | if(codeSize[j]==i){ 256 | huffmanValue.push_back(j); 257 | } 258 | j++; 259 | } 260 | i++; 261 | } 262 | } 263 | 264 | void JpegEncode::countNumbersOfCodesCodedWithKBits(vector&BITS,vector&codeSize){ 265 | for(int i=0;i<257;i++){ 266 | if(codeSize[i]!=0) 267 | BITS[codeSize[i]]++; 268 | 269 | } 270 | } 271 | 272 | void JpegEncode::adjustBitLengthTo16Bits(vector&BITS){ 273 | int i=32,j=0; 274 | while(1){ 275 | if(BITS[i]>0){ 276 | j=i-1; 277 | j--;//Paziti ovdje 278 | while(BITS[j]<=0) 279 | j--; 280 | BITS[i]=BITS[i]-2; 281 | BITS[i-1]=BITS[i-1]+1; 282 | BITS[j+1]=BITS[j+1]+2; 283 | BITS[j]=BITS[j]-1; 284 | continue; 285 | } 286 | else{ 287 | i--; 288 | if(i!=16) 289 | continue; 290 | 291 | while(BITS[i]==0) 292 | i--; 293 | BITS[i]--; 294 | return; 295 | } 296 | } 297 | } 298 | 299 | int JpegEncode::findV(vector&freq,unsigned int v){ 300 | int value=100000000,index=-1; 301 | for(unsigned int i=0;i luminanceZigZagArray; 314 | vector chrominanceCbZigZagArray; 315 | vector chrominanceCrZigZagArray; 316 | 317 | //Prosess every component with FDCT 318 | FDCT(imageToExport.luminance,luminanceZigZagArray,QuantizationTableWriteProcessLuminance.quantizationTableData); 319 | FDCT(imageToExport.chrominanceCb,chrominanceCbZigZagArray,QuantizationTableWriteProcessChrominance.quantizationTableData); 320 | FDCT(imageToExport.chrominanceCr,chrominanceCrZigZagArray,QuantizationTableWriteProcessChrominance.quantizationTableData); 321 | imageToExport.deleteDataFromMatrix();//We don't need data in matrix anymore, all data is saved in zigZag arrays previously declared in this function 322 | //Now we have prepare things for Huffman coding 323 | 324 | /* 325 | * Every DC coefficient is calculated like Difference=DCi-DCi-1 326 | * After that, we calculate category of Difference with getCategoryOfDCTCoefficient() function 327 | * Category is 8 bits long. 328 | * After category, we calculate Difference element bit representation. If Difference is <0 then bit representation is Difference -2^category+1, else bit representation is Difference value. 329 | * 330 | */ 331 | vector luminanceDCElementsFrequency(257,0);//Max category of DC element is 15 332 | vector luminanceACElementsFrequency(257,0);//Max value of 8 bits is 255 so we need vector with 256 elements 333 | luminanceDCElementsFrequency[256]=1; 334 | luminanceACElementsFrequency[256]=1; 335 | 336 | vector luminanceCategoryByte(0);//Here I will save category of every element 337 | vectorluminanceValues(0);//Here I will save bit representation of every value 338 | 339 | 340 | vector chrominanceDCElementsFrequency(257,0);//DC elements frequency for Cb and Cr componenet 341 | vector chrominanceACElementsFrequency(257,0);//AC elements frequency for Cb and Cr component 342 | chrominanceDCElementsFrequency[256]=1; 343 | chrominanceACElementsFrequency[256]=1; 344 | 345 | vector chrominanceCbCategoryByte(0); 346 | vectorchrominanceCbValues(0); 347 | 348 | vector chrominanceCrCategoryByte(0); 349 | vectorchrominanceCrValues(0); 350 | 351 | //Generate frequencies 352 | generateCategoryFrequences(luminanceZigZagArray,luminanceDCElementsFrequency,luminanceACElementsFrequency,luminanceCategoryByte,luminanceValues); 353 | generateCategoryFrequences(chrominanceCbZigZagArray,chrominanceDCElementsFrequency,chrominanceACElementsFrequency,chrominanceCbCategoryByte,chrominanceCbValues); 354 | generateCategoryFrequences(chrominanceCrZigZagArray,chrominanceDCElementsFrequency,chrominanceACElementsFrequency,chrominanceCrCategoryByte,chrominanceCrValues); 355 | 356 | //After this step, we can delete ZigZagArrays of all three components because all data needed is now stored in vectors passed to function generateCategoryFrequences(); 357 | luminanceZigZagArray.clear(); 358 | chrominanceCbZigZagArray.clear(); 359 | chrominanceCrZigZagArray.clear(); 360 | 361 | vectorluminanceDCCodeLenghts(257,0); 362 | vectorluminanceACCodeLenghts(257,0); 363 | vectorluminanceDCBITS(33,0); 364 | vectorluminanceACBITS(33,0); 365 | vectorluminanceDCOthers(257,-1); 366 | vectorluminanceACOthers(257,-1); 367 | vectorluminanceDChuffmanValues; 368 | vectorluminanceAChuffmanValues; 369 | 370 | vectorchrominanceDCCodeLengths(257,0); 371 | vectorchrominanceACCodeLengths(257,0); 372 | vectorchrominanceDCOthers(257,-1); 373 | vectorchrominanceACOthers(257,-1); 374 | vectorchrominanceDCBITS(33,0); 375 | vectorchrominanceACBITS(33,0); 376 | vectorchrominanceDCHuffmanValues; 377 | vectorchrominanceACHuffmanValues; 378 | 379 | 380 | vector luminanceCodesDC(256,-1); 381 | vector luminanceCodesAC(256,-1); 382 | vectorluminanceLengthsDC(256,-1); 383 | vector luminanceLengthsAC(256,-1); 384 | 385 | vector chrominanceCodesDC(256,-1); 386 | vectorchrominanceCodesAC(256,-1); 387 | vector chrominanceLengthsDC(256,-1); 388 | vectorchrominanceLengthsAC(256,-1); 389 | 390 | 391 | 392 | 393 | //Counting for luminance DC elements 394 | generateCodeSizes(luminanceDCElementsFrequency,luminanceDCCodeLenghts,luminanceDCOthers); 395 | countNumbersOfCodesCodedWithKBits(luminanceDCBITS,luminanceDCCodeLenghts); 396 | adjustBitLengthTo16Bits(luminanceDCBITS); 397 | generateBytesAfterBITS(luminanceDCCodeLenghts,luminanceDChuffmanValues); 398 | generateHuffmanCodes(luminanceDCBITS,luminanceDChuffmanValues,luminanceCodesDC,luminanceLengthsDC); 399 | 400 | //Counting for luminance AC elements 401 | generateCodeSizes(luminanceACElementsFrequency,luminanceACCodeLenghts,luminanceACOthers); 402 | countNumbersOfCodesCodedWithKBits(luminanceACBITS,luminanceACCodeLenghts); 403 | adjustBitLengthTo16Bits(luminanceACBITS); 404 | generateBytesAfterBITS(luminanceACCodeLenghts,luminanceAChuffmanValues); 405 | generateHuffmanCodes(luminanceACBITS,luminanceAChuffmanValues,luminanceCodesAC,luminanceLengthsAC);//Number of codes coded with 1,2,3,4....,16 bits are saved in chromianceBITS vector. Concrete values are saved in chrominance(luminance)AC(DC)huffmanValues. Huffman codes for elements is saved in chrominance(luminance)codesAC(DC) and code lengths is stored in chrominance(luminance)AC(DC)CodeLenthts 406 | 407 | 408 | //Counting for chrominance DC elements 409 | generateCodeSizes(chrominanceDCElementsFrequency,chrominanceDCCodeLengths,chrominanceDCOthers); 410 | countNumbersOfCodesCodedWithKBits(chrominanceDCBITS,chrominanceDCCodeLengths); 411 | adjustBitLengthTo16Bits(chrominanceDCBITS); 412 | generateBytesAfterBITS(chrominanceDCCodeLengths,chrominanceDCHuffmanValues); 413 | generateHuffmanCodes(chrominanceDCBITS,chrominanceDCHuffmanValues,chrominanceCodesDC,chrominanceLengthsDC);//Number of codes coded with 1,2,3,4....,16 bits are saved in chromianceBITS vector. Concrete values are saved in chrominance(luminance)AC(DC)huffmanValues. Huffman codes for elements is saved in chrominance(luminance)codesAC(DC) and code lengths is stored in chrominance(luminance)AC(DC)CodeLenthts 414 | 415 | //Counting for chrominance AC elements 416 | generateCodeSizes(chrominanceACElementsFrequency,chrominanceACCodeLengths,chrominanceACOthers); 417 | countNumbersOfCodesCodedWithKBits(chrominanceACBITS,chrominanceACCodeLengths); 418 | adjustBitLengthTo16Bits(chrominanceACBITS); 419 | generateBytesAfterBITS(chrominanceACCodeLengths,chrominanceACHuffmanValues); 420 | generateHuffmanCodes(chrominanceACBITS,chrominanceACHuffmanValues,chrominanceCodesAC,chrominanceLengthsAC);//Number of codes coded with 1,2,3,4....,16 bits are saved in chromianceBITS vector. Concrete values are saved in chrominance(luminance)AC(DC)huffmanValues. Huffman codes for elements is saved in chrominance(luminance)codesAC(DC) and code lengths is stored in chrominance(luminance)AC(DC)CodeLenthts 421 | 422 | 423 | /* 424 | * In jpeg file, there is no huffman table. Rather there is stream of bits like this: 425 | * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - Number of bits (this information is not coded in file, but I write it here for understanding reason) 426 | * 00 00 05 06 00 00 00 01 00 00 00 03 00 00 00 01 after this stream of beats elements starts 02 03 04 05 06 07 08 09 A0 B0 427 | * This means that there are 0 elements coded with 00 bits, 00 elements coded with 1 bit 428 | * 05 elements coded with 2 bits, 06 elements coded with 3 bits etc.... 429 | * Elements coded with 2 bits are: 02 03 04 05 06 430 | * Elements coded with 3 bits are: 07 08 09 A0 B0 .... 431 | * So in my huffman table we have to know how many elements I have coded with 0,1,2,3,4 etc.. bits 432 | * For that reason I developed class HuffmanElementsCount.h In argument codeLength I keep code length, and in vector 433 | * elementsCodedWithCodeLengthBits I keep elements(symbols). This information I need to write in file. 434 | */ 435 | 436 | 437 | 438 | 439 | string fileName=imageToExport.fileName.toStdString(); 440 | 441 | //Here starts file writing process 442 | ofstream output(fileName.c_str(), ios::out | ios::binary); 443 | vectorQtable; 444 | //Write Start of File marker 445 | writeStartOfFileByteInFile(output); 446 | ZigZagCoding(QuantizationTableWriteProcessLuminance.quantizationTableData,Qtable); 447 | //Write Luminance component quantization table 448 | writeQuantizationTablesInFile(output,Qtable,0); 449 | Qtable.clear(); 450 | ZigZagCoding(QuantizationTableWriteProcessChrominance.quantizationTableData,Qtable); 451 | //Write chrominance quantization table 452 | writeQuantizationTablesInFile(output,Qtable,1); 453 | Qtable.clear(); 454 | //Write general picture information (Height, Width, number of components etc....) 455 | writeBaselineDCTInformations(output); 456 | 457 | //write huffman table for luminance component 458 | writeHuffmanTables(output,luminanceDCBITS,luminanceDChuffmanValues,luminanceACBITS,luminanceAChuffmanValues,true); 459 | //write huffman table for chrominance components 460 | writeHuffmanTables(output,chrominanceDCBITS,chrominanceDCHuffmanValues,chrominanceACBITS,chrominanceACHuffmanValues,false); 461 | 462 | //Following vectors are not need anymore so we can delete its data 463 | luminanceDCBITS.clear(); 464 | luminanceDChuffmanValues.clear(); 465 | luminanceACBITS.clear(); 466 | luminanceAChuffmanValues.clear(); 467 | 468 | chrominanceDCBITS.clear(); 469 | chrominanceDCHuffmanValues.clear(); 470 | chrominanceACBITS.clear(); 471 | chrominanceACHuffmanValues.clear(); 472 | 473 | writeScanHeaderMarker(output); 474 | writeScanHeaderData(output,luminanceCodesDC,luminanceCodesAC,luminanceLengthsDC,luminanceLengthsAC,chrominanceCodesDC,chrominanceCodesAC,chrominanceLengthsDC,chrominanceLengthsAC,luminanceCategoryByte,luminanceValues,chrominanceCbCategoryByte,chrominanceCbValues,chrominanceCrCategoryByte,chrominanceCrValues); 475 | writeEOFMarker(output); 476 | return true; 477 | } 478 | 479 | 480 | void JpegEncode::generateHuffmanCodes(vector&BITS,vectorbytes,vector&byteHuffmanCode,vector&byteHuffmanCodeLength){ 481 | int code=0; 482 | int k=0; 483 | for(int i=1;i<17;i++){ 484 | for(int j=0;j &table,int tableID){ 494 | /*Quantization table-specification syntax: 495 | * DQT Lq Pq Tq [Q0 Q1 Q2 ... Q63] 496 | *Number of bits: 16 16 4 4 8 8 8 8 497 | * 498 | * 499 | * DQT - Define Quantization Table marker - Marks the beginning of quantization table-specification parameters (FFDB) 500 | * Lq - Quantization table definition length - Specifies the length of all quantization table parameters 501 | * 502 | * Pq - Quantization table element precision - Specifies the precision of the Qk values. Value 0 indicates 8-bit Qk 503 | * values; value 1 indicates 16-bit Qk values. Pq shall be zero for 8 bit sample precision 504 | * Tq: Quantization table destination identifier - Specifies one of four possible destinations at the decoder into 505 | * which the quantization table shall be installed. 506 | * Qk: Quantization table element - Specifies the kth element out of 64 elements, where k is the index in the zigzag 507 | * ordering of the DCT coefficients. The quantization elements shall be specified in zig-zag scan order. 508 | */ 509 | char a=(char)0xFF; 510 | file.write((char*)&a, 1); 511 | a=(char)0xDB; 512 | file.write((char*)&a, 1); 513 | a=0x00; 514 | file.write((char*)&a, 1); 515 | a=0x43; 516 | file.write((char*)&a, 1); 517 | if(tableID==0){ 518 | a=0x00; 519 | file.write((char*)&a, 1); 520 | } 521 | else{ 522 | a=0x01; 523 | file.write((char*)&a, 1); 524 | } 525 | for(unsigned int i=0;i&BITS,vector&valuesDC,vector&BITSA,vector&valuesAC,bool isLuminance){ 539 | /*General definition of Huffman tables looks like: 540 | * DHT Lh Tc Th [L1 L2 L3 ... L16][V11 V12 ... V1L1 V21 V22 ... V2L2 V161 V162 ... V16L16] 541 | * | | 542 | * | | 543 | * +--------------------------------Multiply with number of huffman tables-----------------+ 544 | * 545 | * Number of Huffman tables is 4: Luminance DC and AC + Chrominance DC and AC 546 | * 547 | * DHT=FFC4 Start of Define Huffman Table marker 548 | * Lh - Huffman table length in bytes (starting from Lh byte) 16 bits long 549 | * Tc - Table class. If we are writing DC elements then this nibble should be 0, in other case 1 ( 4 bits long) 550 | * Th - Huffman table destination identifier. 0 for luminance table, 1 for chrominance table 551 | * Lk - Nuber of bits coded with k bits (8 bits) 552 | * Vi,j - Value associated with each Huffman code - Specifies, for each i, the value associated with each Huffman 553 | * code of length i. The meaning of each value is determined by the Huffman coding model.(8 bits) 554 | */ 555 | 556 | //First, lets count Lh value 557 | bool isFound=false; 558 | int m=0; 559 | for(unsigned int i=0;i>8; 574 | file.write((char*)&byte, 1); 575 | byte=(m&0x000000FF); 576 | file.write((char*)&byte, 1); 577 | 578 | if(isLuminance){ 579 | byte=(char)0x00;//Luminance DC 580 | file.write((char*)&byte, 1); 581 | } 582 | else{ 583 | byte=(char)0x01; 584 | file.write((char*)&byte, 1); 585 | } 586 | 587 | //Writing LUMINANCE DC ELEMENTS 588 | for(int i=1;i<17;i++){ 589 | if(BITS[i]!=0){ 590 | byte=BITS[i]; 591 | file.write((char*)&byte, 1); 592 | isFound=true; 593 | } 594 | 595 | if(isFound==false){ 596 | byte=0x00; 597 | file.write((char*)&byte, 1); 598 | } 599 | isFound=false; 600 | } 601 | 602 | for(unsigned int i=0;i 1, the order of 655 | *interleaved components in the MCU is Cs1 first, Cs2 second, etc. 656 | * 657 | *Tdj: DC entropy coding table destination selector - Specifies one of four possible DC entropy coding table 658 | **destinations from which the entropy table needed for decoding of the DC coefficients of component Csj is 659 | *retrieved 660 | * 661 | **Taj: AC entropy coding table destination selector - Specifies one of four possible AC entropy coding table 662 | *destinations from which the entropy table needed for decoding of the AC coefficients of component Csj is 663 | *retrieved 664 | * 665 | *Ss: Start of spectral or predictor selection - In the DCT modes of operation, this parameter specifies the first 666 | *DCT coefficient in each block in zig-zag order which shall be coded in the scan. This parameter shall be set to 667 | *zero for the sequential DCT processes 668 | * 669 | *Se: End of spectral selection - Specifies the last DCT coefficient in each block in zig-zag order which shall be 670 | **coded in the scan. This parameter shall be set to 63 for the sequential DCT processes. In the lossless mode of 671 | *operations this parameter has no meaning. It shall be set to zero. 672 | * 673 | *Ah: Successive approximation bit position high - This parameter specifies the point transform used in the 674 | *preceding scan (i.e. successive approximation bit position low in the preceding scan) for the band of coefficients 675 | **specified by Ss and Se. This parameter shall be set to zero for the first scan of each band of coefficients. In the 676 | *lossless mode of operations this parameter has no meaning. It shall be set to zero. 677 | * 678 | *Al: Successive approximation bit position low or point transform - In the DCT modes of operation this 679 | ***parameter specifies the point transform, i.e. bit position low, used before coding the band of coefficients 680 | *specified by Ss and Se. This parameter shall be set to zero for the sequential DCT processes. In the lossless 681 | *mode of operations, this parameter specifies the point transform, Pt. 682 | */ 683 | //Write FFDA 684 | char byte=(char)0xFF; 685 | file.write((char*)&byte, 1); 686 | byte=(char)0xDA; 687 | file.write((char*)&byte, 1); 688 | 689 | //Write Lh. There is 12 bytes in Scan header 690 | char *bytes=new char[12]; 691 | bytes[0]=(char)0x00; 692 | bytes[1]=(char)0x0C; 693 | 694 | //Three components: YCbCr 695 | bytes[2]=(char)0x03; 696 | bytes[3]=(char)0x01;//Component Y ID 697 | bytes[4]=(char)0x00; 698 | bytes[5]=(char)0x02;//Component Cb ID 699 | bytes[6]=(char)0x11; 700 | bytes[7]=(char)0x03;//Component Cr ID 701 | bytes[8]=(char)0x11; 702 | bytes[9]=(char)0x00;//Ss=0 703 | bytes[10]=(char)0x3F;//Se=3F=63 704 | bytes[11]=(char)0x00;//Ah=Al=0 705 | file.write((char*)bytes, sizeof(char)*12); 706 | delete bytes; 707 | 708 | } 709 | 710 | void JpegEncode::writeEOFMarker(ofstream &file){ 711 | 712 | char byte=(char)0xFF; 713 | file.write((char*)&byte, sizeof(char)); 714 | byte=(char)0xD9; 715 | file.write((char*)&byte, sizeof(char)); 716 | 717 | } 718 | 719 | 720 | void JpegEncode::writeScanHeaderData(ofstream &file,vector&luminanceCodesDC,vector&luminanceCodesAC,vector&luminanceLengthsDC,vector&luminanceLengthsAC,vector&chrominanceCodesDC,vector&chrominanceCodesAC,vector&chrominanceLengthsDC,vector&chrominanceLengthsAC,vector &luminanceCategoryByte,vector&luminanceValues,vector &chrominanceCbCategoryByte,vector&chrominanceCbValues,vector &chrominanceCrCategoryByte,vector&chrominanceCrValues){ 721 | 722 | unsigned char outputByte=0; 723 | unsigned char byte=0; 724 | int code=0; 725 | int codeLength=0; 726 | int outputNumber=0; 727 | int outputCodeLength=0; 728 | unsigned int i=0,j=0,m=0; 729 | int FF=0x000000000000ffff; 730 | int a=0; 731 | bool isLuminanceComplete=false,isChrominanceCbComplente=false,isChrominanceCrComplete=false; 732 | 733 | while(1){ 734 | //Y component 735 | byte=luminanceCategoryByte[i]; 736 | code=luminanceCodesDC[byte];//Huffman code of byte 737 | codeLength=luminanceLengthsDC[byte];//Huffman code lengh 738 | outputNumber=(outputNumber<>(16-codeLength))&luminanceValues[i]; 743 | outputNumber=(outputNumber<=8){ 748 | outputCodeLength-=8; 749 | outputByte=outputNumber>>outputCodeLength; 750 | file.write((char*)&outputByte,sizeof(char)); 751 | outputNumber=outputNumber-(int(outputByte<=luminanceCategoryByte.size()) 762 | isLuminanceComplete=true; 763 | break; 764 | } 765 | 766 | code=luminanceCodesAC[byte]; 767 | codeLength=luminanceLengthsAC[byte]; 768 | outputNumber=(outputNumber<>(16-codeLength))&luminanceValues[i]; 773 | outputNumber=(outputNumber<=8){ 777 | outputCodeLength-=8; 778 | outputByte=outputNumber>>outputCodeLength; 779 | file.write((char*)&outputByte,sizeof(char)); 780 | outputNumber=outputNumber-(int(outputByte<>(16-codeLength))&chrominanceCbValues[j]; 796 | outputNumber=(outputNumber<=8){ 801 | outputCodeLength-=8; 802 | outputByte=outputNumber>>outputCodeLength; 803 | file.write((char*)&outputByte,sizeof(char)); 804 | outputNumber=outputNumber-(int(outputByte<>(16-codeLength))&chrominanceCbValues[j]; 828 | outputNumber=(outputNumber<=8){ 832 | outputCodeLength-=8; 833 | outputByte=outputNumber>>outputCodeLength; 834 | file.write((char*)&outputByte,sizeof(char)); 835 | outputNumber=outputNumber-(int(outputByte<>(16-codeLength))&chrominanceCrValues[m]; 852 | outputNumber=(outputNumber<=8){ 857 | outputCodeLength-=8; 858 | outputByte=outputNumber>>outputCodeLength; 859 | file.write((char*)&outputByte,sizeof(char)); 860 | outputNumber=outputNumber-(int(outputByte<>(16-codeLength))&chrominanceCrValues[m]; 883 | outputNumber=(outputNumber<=8){ 887 | outputCodeLength-=8; 888 | outputByte=outputNumber>>outputCodeLength; 889 | file.write((char*)&outputByte,sizeof(char)); 890 | outputNumber=outputNumber-(int(outputByte< 0) { 901 | outputNumber = outputNumber << (8-outputCodeLength); 902 | unsigned help=0xFF>>(outputCodeLength); 903 | outputNumber+=help; 904 | outputByte=outputNumber; 905 | file.write((char*)&outputByte,sizeof(char)); 906 | } 907 | } 908 | 909 | 910 | 911 | void JpegEncode::writeBaselineDCTInformations(ofstream &file){ 912 | 913 | /*SOFn: Start of frame marker - Marks the beginning of the frame parameters. The subscript n identifies whether 914 | *the encoding process is baseline sequential, extended sequential, progressive, or lossless, as well as which 915 | *entropy encoding procedure is used.*/ 916 | /*For Baseline DCT process, SOFn frame marker is FFC0*/ 917 | /*Here is general scheme of SOFn block of data*/ 918 | /*FFC0 Lf P Y X Nf [C1 H1 V1 Tq1] [C2 H2 V2 Tq2] ......[Cn Hn Vn Tqn] 919 | *Number of bits 16 16 8 16 16 8 8 4 4 8 920 | *FFC0 is start of Baseline DCT marker 921 | *Lf - frame header length 922 | *P - precision 923 | *Y - Height 924 | *X - Width 925 | *Nf - Number of components (For our case is 3 (YCbCr)) 926 | *C1 - Component ID 927 | *H1 - Horisontal sampling factor (usind in chroma subsamping) 928 | *V1 - Vertical sampling factor (usind in chroma subsamping) 929 | *Tq1- Quantization table ID used for C1 component*/ 930 | char byte=(char)0xFF; 931 | file.write((char*)&byte, 1); 932 | byte=(char)0xC0; 933 | file.write((char*)&byte, 1); 934 | byte=(char)0x00; 935 | file.write((char*)&byte, 1); 936 | byte=(char)0x11;//Lf=17 bytes 937 | file.write((char*)&byte, 1); 938 | byte=(char)0x08;//P=8 939 | file.write((char*)&byte, 1); 940 | 941 | //Y is 16 bits long. I keep height in int variable so I need to do some calculations 942 | 943 | byte=(imageToExport.height&0x0000FFFF)>>8; 944 | file.write((char*)&byte, 1); 945 | byte=(imageToExport.height&0x000000FF); 946 | file.write((char*)&byte, 1); 947 | 948 | //X is 16 bits long. I keep height in int variable so I need to do some calculations 949 | byte=(imageToExport.width&0x0000FFFF)>>8; 950 | file.write((char*)&byte, 1); 951 | byte=(imageToExport.width&0x000000FF); 952 | file.write((char*)&byte, 1); 953 | 954 | //There are three components YCbCr so Nf=3 955 | byte=(char)0x03; 956 | file.write((char*)&byte, 1); 957 | 958 | byte=(char)0x01;//Y component ID is 01 959 | file.write((char*)&byte, 1); 960 | byte=(char)0x11;//H=1 and V=1 961 | file.write((char*)&byte, 1); 962 | byte=(char)0x00;//Table ID for Y is 00 963 | file.write((char*)&byte, 1); 964 | 965 | byte=(char)0x02;//Cb component ID is 02 966 | file.write((char*)&byte, 1); 967 | byte=(char)0x11;//H=1 and V=1 968 | file.write((char*)&byte, 1); 969 | byte=(char)0x01;//Quantization table ID=01 970 | file.write((char*)&byte, 1); 971 | 972 | byte=(char)0x03;//Cr component ID is 02 973 | file.write((char*)&byte, 1); 974 | byte=(char)0x11;//H=1 and V=1 975 | file.write((char*)&byte, 1); 976 | byte=(char)0x01;//Quantization table ID=01 977 | file.write((char*)&byte, 1); 978 | } 979 | 980 | 981 | 982 | 983 | 984 | 985 | void JpegEncode::generateCategoryFrequences(vector&component,vector&DCElementsFrequency,vector &ACElementsFrequency,vector &categoryByte,vector&valueByte){ 986 | bool EOFhit=true; 987 | char category; 988 | int value; 989 | int counter=0; 990 | for(unsigned int i=0;i 10 | #include 11 | #include 12 | #include 13 | #include "component.h" 14 | #include "huffmantable.h" 15 | #include "exportpicture.h" 16 | #include 17 | 18 | #include "imagestate.h" 19 | 20 | 21 | class JpegEncode 22 | { 23 | public: 24 | QuantizationTable QuantizationTableWriteProcessLuminance; 25 | QuantizationTable QuantizationTableWriteProcessChrominance; 26 | //QImage &exportImage; 27 | //QString &fileName; 28 | ExportPicture imageToExport; 29 | void transformToYCbCr(); 30 | void FDCT(vector > &image, vector &zigZagArray, int quantizationTable[8][8]); 31 | void ZigZagCoding(vector > &block8x8,vector&zigZagArray); 32 | void ZigZagCoding(vector > &block8x8,vector&zigZagArray); 33 | void ZigZagCoding(int block8x8[8][8], vector&zigZagArray); 34 | char getCategoryOfDCTCoefficient(int x); 35 | void generateCategoryFrequences(vector&component,vector&DCElementsFrequency,vector &ACElementsFrequency,vector &categoryByte,vector&valueByte); 36 | void writeQuantizationTablesInFile(ofstream &file,vector &table,int tableID); 37 | void writeStartOfFileByteInFile(ofstream &file); 38 | void writeBaselineDCTInformations(ofstream &file); 39 | void writeHuffmanTables(ofstream &file,vector&BITS,vector&valuesDC,vector&BITSA,vector&valuesAC,bool isLuminance); 40 | void writeScanHeaderMarker(ofstream &file); 41 | void writeEOFMarker(ofstream &file); 42 | void writeScanHeaderData(ofstream &file,vector&luminanceCodesDC,vector&luminanceCodesAC,vector&luminanceLengthsDC,vector&luminanceLengthsAC,vector&chrominanceCodesDC,vector&chrominanceCodesAC,vector&chrominanceLengthsDC,vector&chrominanceLengthsAC,vector &luminanceCategoryByte,vector&luminanceValues,vector &chrominanceCbCategoryByte,vector&chrominanceCbValues,vector &chrominanceCrCategoryByte,vector&chrominanceCrValues); 43 | 44 | void generateCodeSizes(vector&freq,vector&codeSize,vector&others); 45 | int findV(vector&freq,unsigned int v); 46 | void countNumbersOfCodesCodedWithKBits(vector&BITS,vector&codeSize); 47 | void adjustBitLengthTo16Bits(vector&BITS); 48 | void generateBytesAfterBITS(vector&codeSize,vector&huffmanValue); 49 | void generateHuffmanCodes(vector&BITS,vectorbytes,vector&byteHuffmanCode,vector&byteHuffmanCodeLength); 50 | 51 | public: 52 | 53 | JpegEncode(QImage &exportImage,const QString& filename); 54 | bool savePicture(); 55 | }; 56 | 57 | #endif // JPEGENCODE_H 58 | -------------------------------------------------------------------------------- /jpegimage.cpp: -------------------------------------------------------------------------------- 1 | #include "jpegimage.h" 2 | #include 3 | 4 | JPEGImage::JPEGImage(): imageFilter("JPEG File (*.jpg)") { 5 | extensionList.push_back( "*.jpeg" ); 6 | extensionList.push_back("*.jpg"); 7 | } 8 | 9 | JPEGImage::~JPEGImage() { 10 | //delete jpeg 11 | } 12 | 13 | void JPEGImage::init(QWidget*) { 14 | } 15 | 16 | void JPEGImage::deinit() { 17 | } 18 | 19 | QString JPEGImage::filter() const { 20 | return imageFilter; 21 | } 22 | 23 | bool JPEGImage::exportImage(const ImageStatePtr imageState, const QString& filename) { 24 | if(imageState->format()!=QImage::Format_RGB888){ 25 | cout<<"Invalid picture format. Picture format must be RGB 888"; 26 | return false; 27 | } 28 | LayerPtr currentLayer=imageState->activeLayer(); 29 | QImage currentPicture(currentLayer->getData(),imageState->width(),imageState->height(),QImage::Format_RGB888); 30 | JpegEncode j(currentPicture,filename); 31 | j.savePicture(); 32 | return true; 33 | } 34 | 35 | QStringList JPEGImage::extensions() const { 36 | return extensionList; 37 | } 38 | 39 | ImageStatePtr JPEGImage::importImage(const QString& filename) { 40 | try { 41 | QFile inputFile(filename); 42 | JpegDecode picture(inputFile); 43 | //return ImageStatePtr(new ImageState(filename, QImage(picture.getImage()))); 44 | return picture.imagePtr; 45 | } catch(const char err[]) { 46 | qDebug()<<"JPEG error: "< 6 | 7 | class QuantizationTable 8 | { 9 | 10 | public: 11 | int tableID; 12 | int quantizationTableData[8][8]; 13 | QuantizationTable(bool writeFileProcess, bool component); 14 | 15 | }; 16 | 17 | #endif // QUANTIZATIONTABLE_H 18 | --------------------------------------------------------------------------------