xref: /haiku/src/kits/translation/BitmapStream.cpp (revision e688bf23d48bfd1216a0cacbdbda5e35a1bcd779)
1 /*
2  * Copyright 2002-2011, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Travis Smith
7  *		Michael Wilber
8  */
9 
10 
11 #include <BitmapStream.h>
12 
13 #include <new>
14 
15 #include <string.h>
16 
17 #include <Bitmap.h>
18 #include <Debug.h>
19 
20 
21 /*!	Initializes this object to either use the BBitmap passed to
22 	it as the object to read/write to or to create a BBitmap
23 	when data is written to this object.
24 
25 	\param bitmap the bitmap used to read from/write to, if it is NULL, a
26 		bitmap is created when this object is written to.
27 */
28 BBitmapStream::BBitmapStream(BBitmap* bitmap)
29 {
30 	fBitmap = bitmap;
31 	fDetached = false;
32 	fPosition = 0;
33 	fSize = 0;
34 	fBigEndianHeader = new (std::nothrow) TranslatorBitmap;
35 	if (fBigEndianHeader == NULL) {
36 		fBitmap = NULL;
37 		return;
38 	}
39 
40 	// Extract header information if bitmap is available
41 	if (fBitmap != NULL && fBitmap->InitCheck() == B_OK) {
42 		fHeader.magic = B_TRANSLATOR_BITMAP;
43 		fHeader.bounds = fBitmap->Bounds();
44 		fHeader.rowBytes = fBitmap->BytesPerRow();
45 		fHeader.colors = fBitmap->ColorSpace();
46 		fHeader.dataSize = static_cast<uint32>
47 			((fHeader.bounds.Height() + 1) * fHeader.rowBytes);
48 		fSize = sizeof(TranslatorBitmap) + fHeader.dataSize;
49 
50 		if (B_HOST_IS_BENDIAN)
51 			memcpy(fBigEndianHeader, &fHeader, sizeof(TranslatorBitmap));
52 		else
53 			SwapHeader(&fHeader, fBigEndianHeader);
54 	} else
55 		fBitmap = NULL;
56 }
57 
58 
59 BBitmapStream::~BBitmapStream()
60 {
61 	if (!fDetached)
62 		delete fBitmap;
63 
64 	delete fBigEndianHeader;
65 }
66 
67 
68 /*!	Reads data from the stream at a specific position and for a
69 	specific amount. The first sizeof(TranslatorBitmap) bytes
70 	are the bitmap header. The header is always written out
71 	and read in as Big Endian byte order.
72 
73 	\param pos, the position in the stream to read from buffer, where the data
74 		will be read into size, the amount of data to read
75 	\return B_ERROR if there is no bitmap stored by the stream, B_BAD_VALUE if
76 		buffer is NULL or pos is invalid or the amount read if the result >= 0
77 */
78 ssize_t
79 BBitmapStream::ReadAt(off_t pos, void* buffer, size_t size)
80 {
81 	if (fBitmap == NULL)
82 		return B_NO_INIT;
83 	if (size == 0)
84 		return B_OK;
85 	if (pos >= (off_t)fSize || pos < 0 || buffer == NULL)
86 		return B_BAD_VALUE;
87 
88 	ssize_t toRead;
89 	void *source;
90 
91 	if (pos < (off_t)sizeof(TranslatorBitmap)) {
92 		toRead = sizeof(TranslatorBitmap) - pos;
93 		source = (reinterpret_cast<uint8 *>(fBigEndianHeader)) + pos;
94 	} else {
95 		toRead = fSize - pos;
96 		source = (reinterpret_cast<uint8 *>(fBitmap->Bits())) + pos -
97 			sizeof(TranslatorBitmap);
98 	}
99 	if (toRead > (ssize_t)size)
100 		toRead = (ssize_t)size;
101 
102 	memcpy(buffer, source, toRead);
103 	return toRead;
104 }
105 
106 
107 /*!	Writes data to the bitmap from data, starting at position pos
108 	of size, size. The first sizeof(TranslatorBitmap) bytes
109 	of data must be the TranslatorBitmap header in the Big
110 	Endian byte order, otherwise, the data will fail to be
111 	successfully written.
112 
113 	\param pos, the position in the stream to write to data, the data to write
114 		to the stream size, the size of the data to write to the stream
115 	\return B_BAD_VALUE if size is bad or data is NULL or pos is invalid,
116 		B_MISMATCHED_VALUES if the bitmap header is bad,
117 		B_ERROR if error allocating memory or setting up big endian header,
118 		or the amount written if the result is >= 0
119 */
120 ssize_t
121 BBitmapStream::WriteAt(off_t pos, const void* data, size_t size)
122 {
123 	if (size == 0)
124 		return B_OK;
125 	if (!data || pos < 0 || pos > (off_t)fSize)
126 		return B_BAD_VALUE;
127 
128 	ssize_t written = 0;
129 	while (size > 0) {
130 		size_t toWrite;
131 		void *dest;
132 		// We depend on writing the header separately in detecting
133 		// changes to it
134 		if (pos < (off_t)sizeof(TranslatorBitmap)) {
135 			toWrite = sizeof(TranslatorBitmap) - pos;
136 			dest = (reinterpret_cast<uint8 *> (&fHeader)) + pos;
137 		} else {
138 			if (fBitmap == NULL || !fBitmap->IsValid())
139 				return B_ERROR;
140 
141 			toWrite = fHeader.dataSize - pos + sizeof(TranslatorBitmap);
142 			dest = (reinterpret_cast<uint8 *> (fBitmap->Bits())) +
143 				pos - sizeof(TranslatorBitmap);
144 		}
145 		if (toWrite > size)
146 			toWrite = size;
147 		if (!toWrite && size)
148 			// i.e. we've been told to write too much
149 			return B_BAD_VALUE;
150 
151 		memcpy(dest, data, toWrite);
152 		pos += toWrite;
153 		written += toWrite;
154 		data = (reinterpret_cast<const uint8 *> (data)) + toWrite;
155 		size -= toWrite;
156 		if (pos > (off_t)fSize)
157 			fSize = pos;
158 		// If we change the header, the rest needs to be reset
159 		if (pos == sizeof(TranslatorBitmap)) {
160 			// Setup both host and Big Endian byte order bitmap headers
161 			memcpy(fBigEndianHeader, &fHeader, sizeof(TranslatorBitmap));
162 			if (B_HOST_IS_LENDIAN)
163 				SwapHeader(fBigEndianHeader, &fHeader);
164 
165 			if (fBitmap != NULL
166 				&& (fBitmap->Bounds() != fHeader.bounds
167 					|| fBitmap->ColorSpace() != fHeader.colors
168 					|| (uint32)fBitmap->BytesPerRow() != fHeader.rowBytes)) {
169 				if (!fDetached)
170 					// if someone detached, we don't delete
171 					delete fBitmap;
172 				fBitmap = NULL;
173 			}
174 			if (fBitmap == NULL) {
175 				if (fHeader.bounds.left > 0.0 || fHeader.bounds.top > 0.0)
176 					DEBUGGER("non-origin bounds!");
177 				fBitmap = new (std::nothrow )BBitmap(fHeader.bounds,
178 					fHeader.colors);
179 				if (fBitmap == NULL)
180 					return B_ERROR;
181 				if (!fBitmap->IsValid()) {
182 					status_t error = fBitmap->InitCheck();
183 					delete fBitmap;
184 					fBitmap = NULL;
185 					return error;
186 				}
187 				if ((uint32)fBitmap->BytesPerRow() != fHeader.rowBytes) {
188 					fprintf(stderr, "BitmapStream %" B_PRId32 " %" B_PRId32 "\n",
189 						fBitmap->BytesPerRow(), fHeader.rowBytes);
190 					return B_MISMATCHED_VALUES;
191 				}
192 			}
193 			if (fBitmap != NULL)
194 				fSize = sizeof(TranslatorBitmap) + fBitmap->BitsLength();
195 		}
196 	}
197 	return written;
198 }
199 
200 
201 /*!	Changes the current stream position.
202 
203 	\param position the position offset
204 	\param seekMode decides how the position offset is used:
205 		SEEK_CUR, position is added to current stream position
206 		SEEK_END, position is added to the end stream position
207 		SEEK_SET, the stream position is set to position
208 	\return B_BAD_VALUE if the position is bad or the new position value if the
209 		result >= 0
210 */
211 off_t
212 BBitmapStream::Seek(off_t position, uint32 seekMode)
213 {
214 	// When whence == SEEK_SET, it just falls through to
215 	// fPosition = position
216 	if (seekMode == SEEK_CUR)
217 		position += fPosition;
218 	else if (seekMode == SEEK_END)
219 		position += fSize;
220 
221 	if (position < 0 || position > (off_t)fSize)
222 		return B_BAD_VALUE;
223 
224 	fPosition = position;
225 	return fPosition;
226 }
227 
228 
229 /*! Returns the current stream position
230 */
231 off_t
232 BBitmapStream::Position() const
233 {
234 	return fPosition;
235 }
236 
237 
238 
239 /*! Returns the curren stream size
240 */
241 off_t
242 BBitmapStream::Size() const
243 {
244 	return fSize;
245 }
246 
247 
248 /*!	Sets the size of the data, but I'm not sure if this function has any real
249 	purpose.
250 
251 	\param size the size to set the stream size to.
252 	\return B_BAD_VALUE, if size is a bad value, B_NO_ERROR, if size is a
253 		valid value
254 */
255 status_t
256 BBitmapStream::SetSize(off_t size)
257 {
258 	if (size < 0)
259 		return B_BAD_VALUE;
260 	if (fBitmap && (size > (off_t)(fHeader.dataSize + sizeof(TranslatorBitmap))))
261 		return B_BAD_VALUE;
262 	// Problem:
263 	// What if someone calls SetSize() before writing the header,
264 	// so we don't know what bitmap to create?
265 	// Solution:
266 	// We assume people will write the header before any data,
267 	// so SetSize() is really not going to do anything.
268 	if (fBitmap != NULL)
269 		fSize = size;
270 
271 	return B_NO_ERROR;
272 }
273 
274 
275 /*!	Returns the internal bitmap through outBitmap so the user
276 	can do whatever they want to it. It means that when the
277 	BBitmapStream is deleted, the bitmap is not deleted. After
278 	the bitmap has been detached, it is still used by the stream,
279 	but it is never deleted by the stream.
280 
281 	\param _bitmap where the bitmap is detached to
282 	\return B_BAD_VALUE, if outBitmap is NULL, B_ERROR, if the bitmap is NULL or
283 		has already been detached, B_OK, if the bitmap was successfully detached
284 */
285 status_t
286 BBitmapStream::DetachBitmap(BBitmap** _bitmap)
287 {
288 	if (_bitmap == NULL)
289 		return B_BAD_VALUE;
290 	if (!fBitmap || fDetached)
291 		return B_ERROR;
292 
293 	fDetached = true;
294 	*_bitmap = fBitmap;
295 
296 	return B_OK;
297 }
298 
299 
300 /*!	Swaps the byte order of source, no matter what the
301 	byte order, and copies the result to destination
302 
303 	\param source data to be swapped
304 	\param destination where the swapped data will be copied to
305 */
306 void
307 BBitmapStream::SwapHeader(const TranslatorBitmap* source,
308 	TranslatorBitmap* destination)
309 {
310 	if (source == NULL || destination == NULL)
311 		return;
312 
313 	memcpy(destination, source, sizeof(TranslatorBitmap));
314 	swap_data(B_UINT32_TYPE, destination, sizeof(TranslatorBitmap),
315 		B_SWAP_ALWAYS);
316 }
317 
318 
319 void BBitmapStream::_ReservedBitmapStream1() {}
320 void BBitmapStream::_ReservedBitmapStream2() {}
321