LibreOffice Module vcl (master) 1
itiff.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <sal/config.h>
21#include <sal/log.hxx>
22
24#include <vcl/graph.hxx>
25#include <vcl/BitmapTools.hxx>
28#include <tools/stream.hxx>
30
31#include <tiffio.h>
32
33#include <filter/TiffReader.hxx>
34
35namespace
36{
37 struct Context
38 {
39 SvStream& rStream;
40 tsize_t nSize;
41 int nShortReads;
42 Context(SvStream& rInStream, tsize_t nInSize)
43 : rStream(rInStream)
44 , nSize(nInSize)
45 , nShortReads(0)
46 {
47 }
48 };
49}
50
51static tsize_t tiff_read(thandle_t handle, tdata_t buf, tsize_t size)
52{
53 Context* pContext = static_cast<Context*>(handle);
54 tsize_t nRead = pContext->rStream.ReadBytes(buf, size);
55 // tdf#149417 allow one short read, which is similar to what
56 // we do for jpeg since tdf#138950
57 if (nRead < size && !pContext->nShortReads)
58 {
59 memset(static_cast<char*>(buf) + nRead, 0, size - nRead);
60 ++pContext->nShortReads;
61 return size;
62 }
63 return nRead;
64}
65
66static tsize_t tiff_write(thandle_t, tdata_t, tsize_t)
67{
68 return -1;
69}
70
71static toff_t tiff_seek(thandle_t handle, toff_t offset, int whence)
72{
73 Context* pContext = static_cast<Context*>(handle);
74
75 switch (whence)
76 {
77 case SEEK_SET:
78 pContext->rStream.Seek(offset);
79 break;
80 case SEEK_CUR:
81 pContext->rStream.SeekRel(offset);
82 break;
83 case SEEK_END:
84 pContext->rStream.Seek(STREAM_SEEK_TO_END);
85 pContext->rStream.SeekRel(offset);
86 break;
87 default:
88 assert(false && "unknown seek type");
89 break;
90 }
91
92 return pContext->rStream.Tell();
93}
94
95static int tiff_close(thandle_t)
96{
97 return 0;
98}
99
100static toff_t tiff_size(thandle_t handle)
101{
102 Context* pContext = static_cast<Context*>(handle);
103 return pContext->nSize;
104}
105
107{
108 auto origErrorHandler = TIFFSetErrorHandler(nullptr);
109 auto origWarningHandler = TIFFSetWarningHandler(nullptr);
110 comphelper::ScopeGuard restoreDefaultHandlers([&]() {
111 TIFFSetErrorHandler(origErrorHandler);
112 TIFFSetWarningHandler(origWarningHandler);
113 });
114
115 Context aContext(rTIFF, rTIFF.remainingSize());
116 TIFF* tif = TIFFClientOpen("libtiff-svstream", "r", &aContext,
119 tiff_size, nullptr, nullptr);
120
121 if (!tif)
122 return false;
123
124 const auto nOrigPos = rTIFF.Tell();
125
126 Animation aAnimation;
127
128 const bool bFuzzing = utl::ConfigManager::IsFuzzing();
129
130 do
131 {
132 uint32_t w, h;
133
134 if (TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w) != 1)
135 {
136 SAL_WARN("filter.tiff", "missing width");
137 break;
138 }
139
140 if (TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h) != 1)
141 {
142 SAL_WARN("filter.tiff", "missing height");
143 break;
144 }
145
146 if (w > SAL_MAX_INT32 / 32 || h > SAL_MAX_INT32 / 32)
147 {
148 SAL_WARN("filter.tiff", "image too large");
149 break;
150 }
151
152 uint32_t nPixelsRequired;
153 constexpr size_t nMaxPixelsAllowed = SAL_MAX_INT32/4;
154 // two buffers currently required, so limit further
155 bool bOk = !o3tl::checked_multiply(w, h, nPixelsRequired) && nPixelsRequired <= nMaxPixelsAllowed / 2;
156 if (!bOk)
157 {
158 SAL_WARN("filter.tiff", "skipping oversized tiff image " << w << " x " << h);
159 break;
160 }
161
162 if (bFuzzing)
163 {
164 const uint64_t MAX_SIZE = 200000000;
165 if (TIFFTileSize64(tif) > MAX_SIZE || nPixelsRequired > MAX_SIZE)
166 {
167 SAL_WARN("filter.tiff", "skipping large tiffs");
168 break;
169 }
170 }
171
172 std::vector<uint32_t> raster(nPixelsRequired);
173 if (TIFFReadRGBAImageOriented(tif, w, h, raster.data(), ORIENTATION_TOPLEFT, 1))
174 {
176 BitmapScopedWriteAccess access(bitmap);
177 if (!access)
178 {
179 SAL_WARN("filter.tiff", "cannot create image " << w << " x " << h);
180 break;
181 }
182
183 AlphaMask bitmapAlpha(Size(w, h));
184 AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
185 if (!accessAlpha)
186 {
187 SAL_WARN("filter.tiff", "cannot create alpha " << w << " x " << h);
188 break;
189 }
190
191 /*
192 ORIENTATION_TOPLEFT = 1
193 ORIENTATION_TOPRIGHT = 2
194 ORIENTATION_BOTRIGHT = 3
195 ORIENTATION_BOTLEFT = 4
196 ORIENTATION_LEFTTOP = 5
197 ORIENTATION_RIGHTTOP = 6
198 ORIENTATION_RIGHTBOT = 7
199 ORIENTATION_LEFTBOT = 8
200 */
201 uint16_t nOrientation;
202 if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &nOrientation) != 1)
203 nOrientation = 0;
204
205 for (uint32_t y = 0; y < h; ++y)
206 {
207 const uint32_t* src = raster.data() + w * y;
208 for (uint32_t x = 0; x < w; ++x)
209 {
210 sal_uInt8 r = TIFFGetR(*src);
211 sal_uInt8 g = TIFFGetG(*src);
212 sal_uInt8 b = TIFFGetB(*src);
213 sal_uInt8 a = TIFFGetA(*src);
214
215 uint32_t dest;
216 switch (nOrientation)
217 {
218 case ORIENTATION_LEFTBOT:
219 dest = w - 1 - x;
220 break;
221 default:
222 dest = x;
223 break;
224 }
225
226 access->SetPixel(y, dest, Color(r, g, b));
227 accessAlpha->SetPixelIndex(y, dest, 255 - a);
228 ++src;
229 }
230 }
231
232 raster.clear();
233
234 access.reset();
235 accessAlpha.reset();
236
237 BitmapEx aBitmapEx(bitmap, bitmapAlpha);
238
239 if (!bFuzzing)
240 {
241 switch (nOrientation)
242 {
243 case ORIENTATION_LEFTBOT:
244 aBitmapEx.Rotate(2700_deg10, COL_BLACK);
245 break;
246 default:
247 break;
248 }
249 }
250
251 AnimationBitmap aAnimationBitmap(aBitmapEx, Point(0, 0), aBitmapEx.GetSizePixel(),
253 aAnimation.Insert(aAnimationBitmap);
254 }
255 } while (TIFFReadDirectory(tif));
256
257 TIFFClose(tif);
258
259 const auto nImages = aAnimation.Count();
260 if (nImages)
261 {
262 if (nImages == 1)
263 rGraphic = aAnimation.GetBitmapEx();
264 else
265 rGraphic = aAnimation;
266
267 // seek to end of TIFF if succeeded
269
270 return true;
271 }
272
273 rTIFF.Seek(nOrigPos);
274 return false;
275}
276
277/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
#define ANIMATION_TIMEOUT_ON_CLICK
Definition: Animation.hxx:28
const BitmapEx & GetBitmapEx() const
Definition: Animation.hxx:59
size_t Count() const
Definition: Animation.hxx:70
bool Insert(const AnimationBitmap &rAnimationBitmap)
Definition: Animation.cxx:393
bool Rotate(Degree10 nAngle10, const Color &rFillColor)
Rotate bitmap by the specified angle.
Definition: BitmapEx.cxx:316
const Size & GetSizePixel() const
Definition: bitmapex.hxx:72
sal_uInt64 Tell() const
sal_uInt64 Seek(sal_uInt64 nPos)
sal_uInt64 remainingSize()
static bool IsFuzzing()
This template handles BitmapAccess the RAII way.
constexpr ::Color COL_BLACK(0x00, 0x00, 0x00)
float y
float x
static tsize_t tiff_write(thandle_t, tdata_t, tsize_t)
Definition: itiff.cxx:66
static int tiff_close(thandle_t)
Definition: itiff.cxx:95
static toff_t tiff_size(thandle_t handle)
Definition: itiff.cxx:100
static toff_t tiff_seek(thandle_t handle, toff_t offset, int whence)
Definition: itiff.cxx:71
bool ImportTiffGraphicImport(SvStream &rTIFF, Graphic &rGraphic)
Definition: itiff.cxx:106
static tsize_t tiff_read(thandle_t handle, tdata_t buf, tsize_t size)
Definition: itiff.cxx:51
uno_Any a
#define SAL_WARN(area, stream)
size
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
sal_Int32 h
sal_Int32 w
#define STREAM_SEEK_TO_END
unsigned char sal_uInt8
#define SAL_MAX_INT32