├── 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