loplugin:constmethod in vcl
[LibreOffice.git] / vcl / source / filter / jpeg / Exif.cxx
blob6dd3bd1b2baa38994d8a9d255d4a29956060a3cf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "Exif.hxx"
21 #include <memory>
22 #include <osl/endian.h>
23 #include <tools/stream.hxx>
25 Exif::Exif() :
26 maOrientation(TOP_LEFT),
27 mbExifPresent(false)
30 Exif::~Exif()
34 void Exif::setOrientation(Orientation aOrientation) {
35 maOrientation = aOrientation;
38 Orientation Exif::convertToOrientation(sal_Int32 value)
40 switch(value) {
41 case 1: return TOP_LEFT;
42 case 2: return TOP_RIGHT;
43 case 3: return BOTTOM_RIGHT;
44 case 4: return BOTTOM_LEFT;
45 case 5: return LEFT_TOP;
46 case 6: return RIGHT_TOP;
47 case 7: return RIGHT_BOTTOM;
48 case 8: return LEFT_BOTTOM;
50 return TOP_LEFT;
53 sal_Int32 Exif::getRotation() const
55 switch(maOrientation) {
56 case TOP_LEFT:
57 return 0;
58 case BOTTOM_RIGHT:
59 return 1800;
60 case RIGHT_TOP:
61 return 2700;
62 case LEFT_BOTTOM:
63 return 900;
64 default:
65 break;
67 return 0;
71 bool Exif::read(SvStream& rStream)
73 sal_Int32 nStreamPosition = rStream.Tell();
74 bool result = processJpeg(rStream, false);
75 rStream.Seek( nStreamPosition );
77 return result;
80 void Exif::write(SvStream& rStream)
82 sal_Int32 nStreamPosition = rStream.Tell();
83 processJpeg(rStream, true);
84 rStream.Seek( nStreamPosition );
87 bool Exif::processJpeg(SvStream& rStream, bool bSetValue)
89 sal_uInt16 aMagic16;
90 sal_uInt16 aLength;
92 sal_uInt32 aSize = rStream.TellEnd();
93 rStream.Seek(STREAM_SEEK_TO_BEGIN);
95 rStream.SetEndian( SvStreamEndian::BIG );
96 rStream.ReadUInt16( aMagic16 );
98 // Compare JPEG magic bytes
99 if( 0xFFD8 != aMagic16 )
101 return false;
104 sal_uInt32 aPreviousPosition = STREAM_SEEK_TO_BEGIN;
106 while(true)
108 sal_uInt8 aMarker = 0xD9;
109 sal_Int32 aCount;
111 for (aCount = 0; aCount < 7; aCount++)
113 rStream.ReadUChar( aMarker );
114 if (aMarker != 0xFF)
116 break;
118 if (aCount >= 6)
120 return false;
124 rStream.ReadUInt16( aLength );
126 if (aLength < 8 || aLength > rStream.remainingSize())
128 return false;
131 if (aMarker == 0xE1)
133 return processExif(rStream, aLength, bSetValue);
135 else if (aMarker == 0xD9)
137 return false;
139 else
141 sal_uInt32 aCurrentPosition = rStream.SeekRel(aLength-1);
142 if (aCurrentPosition == aPreviousPosition || aCurrentPosition > aSize)
144 return false;
146 aPreviousPosition = aCurrentPosition;
149 return false;
152 namespace {
154 sal_uInt16 read16(sal_uInt8 const (& data)[2], bool littleEndian) {
155 if (littleEndian) {
156 return data[0] | (sal_uInt16(data[1]) << 8);
157 } else {
158 return data[1] | (sal_uInt16(data[0]) << 8);
162 void write16(sal_uInt16 value, sal_uInt8 (& data)[2], bool littleEndian) {
163 if (littleEndian) {
164 data[0] = value & 0xFF;
165 data[1] = value >> 8;
166 } else {
167 data[1] = value & 0xFF;
168 data[0] = value >> 8;
172 sal_uInt32 read32(sal_uInt8 const (& data)[4], bool littleEndian) {
173 if (littleEndian) {
174 return data[0] | (sal_uInt32(data[1]) << 8)
175 | (sal_uInt32(data[2]) << 16) | (sal_uInt32(data[3]) << 24);
176 } else {
177 return data[3] | (sal_uInt32(data[2]) << 8)
178 | (sal_uInt32(data[1]) << 16) | (sal_uInt32(data[0]) << 24);
182 void write32(sal_uInt32 value, sal_uInt8 (& data)[4], bool littleEndian) {
183 if (littleEndian) {
184 data[0] = value & 0xFF;
185 data[1] = (value >> 8) & 0xFF;
186 data[2] = (value >> 16) & 0xFF;
187 data[3] = value >> 24;
188 } else {
189 data[3] = value & 0xFF;
190 data[2] = (value >> 8) & 0xFF;
191 data[1] = (value >> 16) & 0xFF;
192 data[0] = value >> 24;
198 void Exif::processIFD(sal_uInt8* pExifData, sal_uInt16 aLength, sal_uInt16 aOffset, sal_uInt16 aNumberOfTags, bool bSetValue, bool littleEndian)
200 ExifIFD* ifd = nullptr;
202 while (aOffset <= aLength - 12 && aNumberOfTags > 0)
204 ifd = reinterpret_cast<ExifIFD*>(&pExifData[aOffset]);
205 sal_uInt16 tag = read16(ifd->tag, littleEndian);
207 if (tag == ORIENTATION)
209 if(bSetValue)
211 write16(3, ifd->type, littleEndian);
212 write32(1, ifd->count, littleEndian);
213 write32(maOrientation, ifd->offset, littleEndian);
215 else
217 sal_uInt32 nIfdOffset = read32(ifd->offset, littleEndian);
218 maOrientation = convertToOrientation(nIfdOffset);
222 aNumberOfTags--;
223 aOffset += 12;
227 bool Exif::processExif(SvStream& rStream, sal_uInt16 aSectionLength, bool bSetValue)
229 sal_uInt32 aMagic32;
230 sal_uInt16 aMagic16;
232 rStream.ReadUInt32( aMagic32 );
233 rStream.ReadUInt16( aMagic16 );
235 // Compare EXIF magic bytes
236 if( 0x45786966 != aMagic32 || 0x0000 != aMagic16)
238 return false;
241 sal_uInt16 aLength = aSectionLength - 6; // Length = Section - Header
243 std::unique_ptr<sal_uInt8[]> aExifData(new sal_uInt8[aLength]);
244 sal_uInt32 aExifDataBeginPosition = rStream.Tell();
246 rStream.ReadBytes(aExifData.get(), aLength);
248 // Exif detected
249 mbExifPresent = true;
251 TiffHeader* aTiffHeader = reinterpret_cast<TiffHeader*>(&aExifData[0]);
253 bool bIntel = aTiffHeader->byteOrder == 0x4949; //little-endian
254 bool bMotorola = aTiffHeader->byteOrder == 0x4D4D; //big-endian
256 if (!bIntel && !bMotorola)
258 return false;
261 bool bSwap = false;
263 #ifdef OSL_BIGENDIAN
264 if (bIntel)
265 bSwap = true;
266 #else
267 if (bMotorola)
268 bSwap = true;
269 #endif
271 if (bSwap)
273 aTiffHeader->tagAlign = OSL_SWAPWORD(aTiffHeader->tagAlign);
274 aTiffHeader->offset = OSL_SWAPDWORD(aTiffHeader->offset);
277 if (aTiffHeader->tagAlign != 0x002A) // TIFF tag
279 return false;
282 sal_uInt16 aOffset = aTiffHeader->offset;
284 sal_uInt16 aNumberOfTags = aExifData[aOffset];
285 if (bSwap)
287 aNumberOfTags = ((aExifData[aOffset] << 8) | aExifData[aOffset+1]);
290 processIFD(aExifData.get(), aLength, aOffset+2, aNumberOfTags, bSetValue, bIntel);
292 if (bSetValue)
294 rStream.Seek(aExifDataBeginPosition);
295 rStream.WriteBytes(aExifData.get(), aLength);
298 return true;
301 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */