1 // -*- mode: C++; tab-width: 4 -*-
2 // vi: ts=4
3 
4 /*
5  * Copyright (c) 2010, Patrick A. Palmer and Leszek Godlewski.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  *   - Redistributions of source code must retain the above copyright notice,
12  *     this list of conditions and the following disclaimer.
13  *
14  *   - Redistributions in binary form must reproduce the above copyright
15  *     notice, this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *
18  *   - Neither the name of Patrick A. Palmer nor the names of its
19  *     contributors may be used to endorse or promote products derived from
20  *     this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <cstdio>
36 #include <cstring>
37 #include <ctime>
38 #include <cassert>
39 
40 #include "Cineon.h"
41 #include "EndianSwap.h"
42 #include "ReaderInternal.h"
43 #include "ElementReadStream.h"
44 #include "Codec.h"
45 
46 
Reader()47 cineon::Reader::Reader() : fd(0), rio(0)
48 {
49 	// initialize all of the Codec* to NULL
50 	this->codec = 0;
51 }
52 
53 
~Reader()54 cineon::Reader::~Reader()
55 {
56 	this->Reset();
57     delete this->rio;
58 }
59 
60 
Reset()61 void cineon::Reader::Reset()
62 {
63 	// delete all of the Codec * entries
64 	delete this->codec;
65 	this->codec = 0;
66 
67 	// Element Reader
68 	if (this->rio)
69 	{
70 		delete rio;
71 		this->rio = 0;
72 	}
73 	if (this->fd)
74 		this->rio = new ElementReadStream(this->fd);
75 }
76 
77 
SetInStream(InStream * fd)78 void cineon::Reader::SetInStream(InStream *fd)
79 {
80 	this->fd = fd;
81 	this->Reset();
82 }
83 
84 
ReadHeader()85 bool cineon::Reader::ReadHeader()
86 {
87 	return this->header.Read(this->fd);
88 }
89 
90 
ReadImage(void * data,const DataSize size)91 bool cineon::Reader::ReadImage(void *data, const DataSize size)
92 {
93 	Block block(0, 0, this->header.Width()-1, this->header.Height()-1);
94 	return this->ReadBlock(data, size, block);
95 }
96 
97 
98 
99 /*
100 	implementation notes:
101 
102 	cineon::readBlock reads in the image starting from the beginning of the channel.  This can
103 	be optimized if the image isn't encoded; we can skip forward in the file to close to the
104 	start of (block.x1, block.y1) and determine exactly which bit will be the start.  This
105 	certainly will save some time for cases where we are only reading ROIs (regions of interest).
106 
107 */
108 
ReadBlock(void * data,const DataSize size,Block & block)109 bool cineon::Reader::ReadBlock(void *data, const DataSize size, Block &block)
110 {
111 	int i;
112 
113 	// check the block coordinates
114 	block.Check();
115 
116 	// get the number of components for this element descriptor
117 	const int numberOfComponents = this->header.NumberOfElements();
118 
119 	// check the widths and bit depths of the image elements
120 	bool consistentDepth = true;
121 	bool consistentWidth = true;
122 	const int bitDepth = this->header.BitDepth(0);
123 	const int width = this->header.PixelsPerLine(0);
124 	for (i = 1; i < numberOfComponents; i++) {
125 		if (this->header.BitDepth(i) != bitDepth) {
126 			consistentDepth = false;
127 			if (!consistentWidth)
128 				break;
129 		}
130 		if ((int)this->header.PixelsPerLine(i) != width) {
131 			consistentWidth = false;
132 			if (!consistentDepth)
133 				break;
134 		}
135 	}
136 
137 	// lets see if this can be done in a single fast read
138 	if (consistentDepth && consistentWidth && this->header.EndOfLinePadding() == 0 &&
139 		((bitDepth == 8 && size == cineon::kByte) ||
140 		 (bitDepth == 16 && size == cineon::kWord) ||
141 		 (bitDepth == 32 && size == cineon::kInt) ||
142 		 (bitDepth == 64 && size == cineon::kLongLong)) &&
143 		block.x1 == 0 && block.x2 == (int)(this->header.Width()-1))
144 	{
145 		// seek to the beginning of the image block
146 		if (this->fd->Seek((this->header.ImageOffset() + (block.y1 * this->header.Width() * (bitDepth / 8) * numberOfComponents)), InStream::kStart) == false)
147 			return false;
148 
149 		// size of the image
150 		const size_t imageSize = this->header.Width() * (block.y2 - block.y1 + 1) * numberOfComponents;
151 		const size_t imageByteSize = imageSize * bitDepth / 8;
152 
153 		size_t rs = this->fd->ReadDirect(data, imageByteSize);
154 		if (rs != imageByteSize)
155 			return false;
156 
157 		// swap the bytes if different byte order
158 		if (this->header.RequiresByteSwap())
159 			cineon::EndianSwapImageBuffer(size, data, imageSize);
160 
161 		return true;
162 	}
163 
164 
165 	// determine if the encoding system is loaded
166 	if (this->codec == 0)
167 		// this element reader has not been used
168 		this->codec = new Codec;
169 
170 	// read the image block
171 	return this->codec->Read(this->header, this->rio, block, data, size);
172 }
173 
174 
175 
ReadUserData(unsigned char * data)176 bool cineon::Reader::ReadUserData(unsigned char *data)
177 {
178 	// check to make sure there is some user data
179 	if (this->header.UserSize() == 0)
180 		return true;
181 
182 	// seek to the beginning of the user data block
183 	if (this->fd->Seek(sizeof(GenericHeader) + sizeof(IndustryHeader), InStream::kStart) == false)
184 		return false;
185 
186 	size_t rs = this->fd->ReadDirect(data, this->header.UserSize());
187 	if (rs != this->header.UserSize())
188 		return false;
189 
190 	return true;
191 }
192 
193 
194 
195