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