1 /******************************************************************************
2  * $Id: tif_ovrcache.c,v 1.11 2015-05-29 03:08:19 bfriesen Exp $
3  *
4  * Project:  TIFF Overview Builder
5  * Purpose:  Library functions to maintain two rows of tiles or two strips
6  *           of data for output overviews as an output cache.
7  * Author:   Frank Warmerdam, warmerdam@pobox.com
8  *
9  ******************************************************************************
10  * Copyright (c) 2000, Frank Warmerdam
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ******************************************************************************
30  */
31 
32 #include "tiffiop.h"
33 #include "tif_ovrcache.h"
34 #include <assert.h>
35 
36 /************************************************************************/
37 /*                         TIFFCreateOvrCache()                         */
38 /*                                                                      */
39 /*      Create an overview cache to hold two rows of blocks from an     */
40 /*      existing TIFF directory.                                        */
41 /************************************************************************/
42 
TIFFCreateOvrCache(TIFF * hTIFF,toff_t nDirOffset)43 TIFFOvrCache *TIFFCreateOvrCache( TIFF *hTIFF, toff_t nDirOffset )
44 
45 {
46     TIFFOvrCache	*psCache;
47     toff_t		nBaseDirOffset;
48 
49     psCache = (TIFFOvrCache *) _TIFFmalloc(sizeof(TIFFOvrCache));
50     psCache->nDirOffset = nDirOffset;
51     psCache->hTIFF = hTIFF;
52 
53 /* -------------------------------------------------------------------- */
54 /*      Get definition of this raster from the TIFF file itself.        */
55 /* -------------------------------------------------------------------- */
56     nBaseDirOffset = TIFFCurrentDirOffset( psCache->hTIFF );
57     TIFFSetSubDirectory( hTIFF, nDirOffset );
58 
59     TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &(psCache->nXSize) );
60     TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &(psCache->nYSize) );
61 
62     TIFFGetField( hTIFF, TIFFTAG_BITSPERSAMPLE, &(psCache->nBitsPerPixel) );
63     TIFFGetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL, &(psCache->nSamples) );
64     TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &(psCache->nPlanarConfig) );
65 
66     if( !TIFFIsTiled( hTIFF ) )
67     {
68         TIFFGetField( hTIFF, TIFFTAG_ROWSPERSTRIP, &(psCache->nBlockYSize) );
69         psCache->nBlockXSize = psCache->nXSize;
70         psCache->nBytesPerBlock = TIFFStripSize(hTIFF);
71         psCache->bTiled = FALSE;
72     }
73     else
74     {
75         TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &(psCache->nBlockXSize) );
76         TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &(psCache->nBlockYSize) );
77         psCache->nBytesPerBlock = TIFFTileSize(hTIFF);
78         psCache->bTiled = TRUE;
79     }
80 
81 /* -------------------------------------------------------------------- */
82 /*      Compute some values from this.                                  */
83 /* -------------------------------------------------------------------- */
84 
85     psCache->nBlocksPerRow = (psCache->nXSize + psCache->nBlockXSize - 1)
86         		/ psCache->nBlockXSize;
87     psCache->nBlocksPerColumn = (psCache->nYSize + psCache->nBlockYSize - 1)
88         		/ psCache->nBlockYSize;
89 
90     if (psCache->nPlanarConfig == PLANARCONFIG_SEPARATE)
91         psCache->nBytesPerRow = psCache->nBytesPerBlock
92             * psCache->nBlocksPerRow * psCache->nSamples;
93     else
94         psCache->nBytesPerRow =
95             psCache->nBytesPerBlock * psCache->nBlocksPerRow;
96 
97 
98 /* -------------------------------------------------------------------- */
99 /*      Allocate and initialize the data buffers.                       */
100 /* -------------------------------------------------------------------- */
101 
102     psCache->pabyRow1Blocks =
103         (unsigned char *) _TIFFmalloc(psCache->nBytesPerRow);
104     psCache->pabyRow2Blocks =
105         (unsigned char *) _TIFFmalloc(psCache->nBytesPerRow);
106 
107     if ( psCache->pabyRow1Blocks == NULL
108          || psCache->pabyRow2Blocks == NULL )
109     {
110 		TIFFErrorExt( hTIFF->tif_clientdata, hTIFF->tif_name,
111 					  "Can't allocate memory for overview cache." );
112                 /* TODO: use of TIFFError is inconsistent with use of fprintf in addtiffo.c, sort out */
113                 if (psCache->pabyRow1Blocks) _TIFFfree(psCache->pabyRow1Blocks);
114                 if (psCache->pabyRow2Blocks) _TIFFfree(psCache->pabyRow2Blocks);
115                 _TIFFfree( psCache );
116         return NULL;
117     }
118 
119     _TIFFmemset( psCache->pabyRow1Blocks, 0, psCache->nBytesPerRow );
120     _TIFFmemset( psCache->pabyRow2Blocks, 0, psCache->nBytesPerRow );
121 
122     psCache->nBlockOffset = 0;
123 
124     TIFFSetSubDirectory( psCache->hTIFF, nBaseDirOffset );
125 
126     return psCache;
127 }
128 
129 /************************************************************************/
130 /*                          TIFFWriteOvrRow()                           */
131 /*                                                                      */
132 /*      Write one entire row of blocks (row 1) to the tiff file, and    */
133 /*      then rotate the block buffers, essentially moving things        */
134 /*      down by one block.                                              */
135 /************************************************************************/
136 
TIFFWriteOvrRow(TIFFOvrCache * psCache)137 static void TIFFWriteOvrRow( TIFFOvrCache * psCache )
138 
139 {
140     int		nRet, iTileX, iTileY = psCache->nBlockOffset;
141     unsigned char *pabyData;
142     toff_t	nBaseDirOffset;
143     uint32      RowsInStrip;
144 
145 /* -------------------------------------------------------------------- */
146 /*      If the output cache is multi-byte per sample, and the file      */
147 /*      being written to is of a different byte order than the current  */
148 /*      platform, we will need to byte swap the data.                   */
149 /* -------------------------------------------------------------------- */
150     if( TIFFIsByteSwapped(psCache->hTIFF) )
151     {
152         if( psCache->nBitsPerPixel == 16 )
153             TIFFSwabArrayOfShort( (uint16 *) psCache->pabyRow1Blocks,
154                       (psCache->nBytesPerBlock * psCache->nSamples) / 2 );
155 
156         else if( psCache->nBitsPerPixel == 32 )
157             TIFFSwabArrayOfLong( (uint32 *) psCache->pabyRow1Blocks,
158                          (psCache->nBytesPerBlock * psCache->nSamples) / 4 );
159 
160         else if( psCache->nBitsPerPixel == 64 )
161             TIFFSwabArrayOfDouble( (double *) psCache->pabyRow1Blocks,
162                          (psCache->nBytesPerBlock * psCache->nSamples) / 8 );
163     }
164 
165 /* -------------------------------------------------------------------- */
166 /*      Record original directory position, so we can restore it at     */
167 /*      end.                                                            */
168 /* -------------------------------------------------------------------- */
169     nBaseDirOffset = TIFFCurrentDirOffset( psCache->hTIFF );
170     nRet = TIFFSetSubDirectory( psCache->hTIFF, psCache->nDirOffset );
171     assert( nRet == 1 );
172 
173 /* -------------------------------------------------------------------- */
174 /*      Write blocks to TIFF file.                                      */
175 /* -------------------------------------------------------------------- */
176 	for( iTileX = 0; iTileX < psCache->nBlocksPerRow; iTileX++ )
177 	{
178 		int nTileID;
179 
180 		if (psCache->nPlanarConfig == PLANARCONFIG_SEPARATE)
181 		{
182 			int iSample;
183 
184 			for( iSample = 0; iSample < psCache->nSamples; iSample++ )
185 			{
186 				pabyData = TIFFGetOvrBlock( psCache, iTileX, iTileY, iSample );
187 
188 				if( psCache->bTiled )
189 				{
190 					nTileID = TIFFComputeTile( psCache->hTIFF,
191 					    iTileX * psCache->nBlockXSize,
192 					    iTileY * psCache->nBlockYSize,
193 					    0, (tsample_t) iSample );
194 					TIFFWriteEncodedTile( psCache->hTIFF, nTileID,
195 					    pabyData,
196 					    TIFFTileSize(psCache->hTIFF) );
197 				}
198 				else
199 				{
200 					nTileID = TIFFComputeStrip( psCache->hTIFF,
201 					    iTileY * psCache->nBlockYSize,
202 					    (tsample_t) iSample );
203 					RowsInStrip=psCache->nBlockYSize;
204 					if ((iTileY+1)*psCache->nBlockYSize>psCache->nYSize)
205 						RowsInStrip=psCache->nYSize-iTileY*psCache->nBlockYSize;
206 					TIFFWriteEncodedStrip( psCache->hTIFF, nTileID,
207 					    pabyData,
208 					    TIFFVStripSize(psCache->hTIFF,RowsInStrip) );
209 				}
210 			}
211 
212 		}
213 		else
214 		{
215 			pabyData = TIFFGetOvrBlock( psCache, iTileX, iTileY, 0 );
216 
217 			if( psCache->bTiled )
218 			{
219 				nTileID = TIFFComputeTile( psCache->hTIFF,
220 				    iTileX * psCache->nBlockXSize,
221 				    iTileY * psCache->nBlockYSize,
222 				    0, 0 );
223 				TIFFWriteEncodedTile( psCache->hTIFF, nTileID,
224 				    pabyData,
225 				    TIFFTileSize(psCache->hTIFF) );
226 			}
227 			else
228 			{
229 				nTileID = TIFFComputeStrip( psCache->hTIFF,
230 				    iTileY * psCache->nBlockYSize,
231 				    0 );
232 				RowsInStrip=psCache->nBlockYSize;
233 				if ((iTileY+1)*psCache->nBlockYSize>psCache->nYSize)
234 					RowsInStrip=psCache->nYSize-iTileY*psCache->nBlockYSize;
235 				TIFFWriteEncodedStrip( psCache->hTIFF, nTileID,
236 				    pabyData,
237 				    TIFFVStripSize(psCache->hTIFF,RowsInStrip) );
238 			}
239 		}
240 	}
241 	/* TODO: add checks on error status return of TIFFWriteEncodedTile and TIFFWriteEncodedStrip */
242 
243 /* -------------------------------------------------------------------- */
244 /*      Rotate buffers.                                                 */
245 /* -------------------------------------------------------------------- */
246     pabyData = psCache->pabyRow1Blocks;
247     psCache->pabyRow1Blocks = psCache->pabyRow2Blocks;
248     psCache->pabyRow2Blocks = pabyData;
249 
250     _TIFFmemset( pabyData, 0, psCache->nBytesPerRow );
251 
252     psCache->nBlockOffset++;
253 
254 /* -------------------------------------------------------------------- */
255 /*      Restore access to original directory.                           */
256 /* -------------------------------------------------------------------- */
257     TIFFFlush( psCache->hTIFF );
258     /* TODO: add checks on error status return of TIFFFlush */
259     TIFFSetSubDirectory( psCache->hTIFF, nBaseDirOffset );
260     /* TODO: add checks on error status return of TIFFSetSubDirectory */
261 }
262 
263 /************************************************************************/
264 /*                          TIFFGetOvrBlock()                           */
265 /************************************************************************/
266 
267 /* TODO: make TIFF_Downsample handle iSample offset, so that we can
268  * do with a single TIFFGetOvrBlock and no longer need TIFFGetOvrBlock_Subsampled */
TIFFGetOvrBlock(TIFFOvrCache * psCache,int iTileX,int iTileY,int iSample)269 unsigned char *TIFFGetOvrBlock( TIFFOvrCache *psCache, int iTileX, int iTileY,
270                                 int iSample )
271 
272 {
273     long	       nRowOffset;
274 
275     if ( iTileY > psCache->nBlockOffset + 1 )
276         TIFFWriteOvrRow( psCache );
277 
278     assert( iTileX >= 0 && iTileX < psCache->nBlocksPerRow );
279     assert( iTileY >= 0 && iTileY < psCache->nBlocksPerColumn );
280     assert( iTileY >= psCache->nBlockOffset
281             && iTileY < psCache->nBlockOffset+2 );
282     assert( iSample >= 0 && iSample < psCache->nSamples );
283 
284     if (psCache->nPlanarConfig == PLANARCONFIG_SEPARATE)
285         nRowOffset = ((((toff_t) iTileX * psCache->nSamples) + iSample)
286                       * psCache->nBytesPerBlock);
287     else
288         nRowOffset = iTileX * psCache->nBytesPerBlock +
289             (psCache->nBitsPerPixel + 7) / 8 * iSample;
290 
291     if ( iTileY == psCache->nBlockOffset )
292         return psCache->pabyRow1Blocks + nRowOffset;
293     else
294         return psCache->pabyRow2Blocks + nRowOffset;
295 }
296 
297 /************************************************************************/
298 /*                     TIFFGetOvrBlock_Subsampled()                     */
299 /************************************************************************/
300 
TIFFGetOvrBlock_Subsampled(TIFFOvrCache * psCache,int iTileX,int iTileY)301 unsigned char *TIFFGetOvrBlock_Subsampled( TIFFOvrCache *psCache,
302                                            int iTileX, int iTileY )
303 
304 {
305     int		nRowOffset;
306 
307     if( iTileY > psCache->nBlockOffset + 1 )
308         TIFFWriteOvrRow( psCache );
309 
310     assert( iTileX >= 0 && iTileX < psCache->nBlocksPerRow );
311     assert( iTileY >= 0 && iTileY < psCache->nBlocksPerColumn );
312     assert( iTileY >= psCache->nBlockOffset
313             && iTileY < psCache->nBlockOffset+2 );
314     assert( psCache->nPlanarConfig != PLANARCONFIG_SEPARATE );
315 
316     nRowOffset = iTileX * psCache->nBytesPerBlock;
317 
318     if( iTileY == psCache->nBlockOffset )
319         return psCache->pabyRow1Blocks + nRowOffset;
320     else
321         return psCache->pabyRow2Blocks + nRowOffset;
322 }
323 
324 /************************************************************************/
325 /*                        TIFFDestroyOvrCache()                         */
326 /************************************************************************/
327 
TIFFDestroyOvrCache(TIFFOvrCache * psCache)328 void TIFFDestroyOvrCache( TIFFOvrCache * psCache )
329 
330 {
331     while( psCache->nBlockOffset < psCache->nBlocksPerColumn )
332         TIFFWriteOvrRow( psCache );
333 
334     _TIFFfree( psCache->pabyRow1Blocks );
335     _TIFFfree( psCache->pabyRow2Blocks );
336     _TIFFfree( psCache );
337 }
338 /*
339  * Local Variables:
340  * mode: c
341  * c-basic-offset: 4
342  * fill-column: 78
343  * End:
344  */
345