xref: /haiku/src/kits/translation/BitmapStream.cpp (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
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 BBitmapStream::BBitmapStream(BBitmap* bitmap)
25 {
26 	fBitmap = bitmap;
27 	fDetached = false;
28 	fPosition = 0;
29 	fSize = 0;
30 	fBigEndianHeader = new (std::nothrow) TranslatorBitmap;
31 	if (fBigEndianHeader == NULL) {
32 		fBitmap = NULL;
33 		return;
34 	}
35 
36 	// Extract header information if bitmap is available
37 	if (fBitmap != NULL && fBitmap->InitCheck() == B_OK) {
38 		fHeader.magic = B_TRANSLATOR_BITMAP;
39 		fHeader.bounds = fBitmap->Bounds();
40 		fHeader.rowBytes = fBitmap->BytesPerRow();
41 		fHeader.colors = fBitmap->ColorSpace();
42 		fHeader.dataSize = static_cast<uint32>
43 			((fHeader.bounds.Height() + 1) * fHeader.rowBytes);
44 		fSize = sizeof(TranslatorBitmap) + fHeader.dataSize;
45 
46 		if (B_HOST_IS_BENDIAN)
47 			*fBigEndianHeader = fHeader;
48 		else
49 			SwapHeader(&fHeader, fBigEndianHeader);
50 	} else
51 		fBitmap = NULL;
52 }
53 
54 
55 BBitmapStream::~BBitmapStream()
56 {
57 	if (!fDetached)
58 		delete fBitmap;
59 
60 	delete fBigEndianHeader;
61 }
62 
63 
64 // Reads data from the stream at a specific position and size.
65 ssize_t
66 BBitmapStream::ReadAt(off_t pos, void* buffer, size_t size)
67 {
68 	if (fBitmap == NULL)
69 		return B_NO_INIT;
70 	if (size == 0)
71 		return B_OK;
72 	if (pos >= (off_t)fSize || pos < 0 || buffer == NULL)
73 		return B_BAD_VALUE;
74 
75 	ssize_t toRead;
76 	void *source;
77 
78 	if (pos < (off_t)sizeof(TranslatorBitmap)) {
79 		toRead = sizeof(TranslatorBitmap) - pos;
80 		source = (reinterpret_cast<uint8 *>(fBigEndianHeader)) + pos;
81 	} else {
82 		toRead = fSize - pos;
83 		source = (reinterpret_cast<uint8 *>(fBitmap->Bits())) + pos -
84 			sizeof(TranslatorBitmap);
85 	}
86 	if (toRead > (ssize_t)size)
87 		toRead = (ssize_t)size;
88 
89 	memcpy(buffer, source, toRead);
90 	return toRead;
91 }
92 
93 
94 // Writes data to the bitmap starting at a specific position and size.
95 ssize_t
96 BBitmapStream::WriteAt(off_t pos, const void* data, size_t size)
97 {
98 	if (size == 0)
99 		return B_OK;
100 	if (!data || pos < 0 || pos > (off_t)fSize)
101 		return B_BAD_VALUE;
102 
103 	ssize_t written = 0;
104 	while (size > 0) {
105 		size_t toWrite;
106 		void *dest;
107 		// We depend on writing the header separately in detecting
108 		// changes to it
109 		if (pos < (off_t)sizeof(TranslatorBitmap)) {
110 			toWrite = sizeof(TranslatorBitmap) - pos;
111 			dest = (reinterpret_cast<uint8 *> (&fHeader)) + pos;
112 		} else {
113 			if (fBitmap == NULL || !fBitmap->IsValid())
114 				return B_ERROR;
115 
116 			toWrite = fHeader.dataSize - pos + sizeof(TranslatorBitmap);
117 			dest = (reinterpret_cast<uint8 *> (fBitmap->Bits())) +
118 				pos - sizeof(TranslatorBitmap);
119 		}
120 		if (toWrite > size)
121 			toWrite = size;
122 		if (!toWrite && size)
123 			// i.e. we've been told to write too much
124 			return B_BAD_VALUE;
125 
126 		memcpy(dest, data, toWrite);
127 		pos += toWrite;
128 		written += toWrite;
129 		data = (reinterpret_cast<const uint8 *> (data)) + toWrite;
130 		size -= toWrite;
131 		if (pos > (off_t)fSize)
132 			fSize = pos;
133 		// If we change the header, the rest needs to be reset
134 		if (pos == sizeof(TranslatorBitmap)) {
135 			// Setup both host and Big Endian byte order bitmap headers
136 			*fBigEndianHeader = fHeader;
137 			if (B_HOST_IS_LENDIAN)
138 				SwapHeader(fBigEndianHeader, &fHeader);
139 
140 			if (fBitmap != NULL
141 				&& (fBitmap->Bounds() != fHeader.bounds
142 					|| fBitmap->ColorSpace() != fHeader.colors
143 					|| (uint32)fBitmap->BytesPerRow() != fHeader.rowBytes)) {
144 				if (!fDetached)
145 					// if someone detached, we don't delete
146 					delete fBitmap;
147 				fBitmap = NULL;
148 			}
149 			if (fBitmap == NULL) {
150 				if (fHeader.bounds.left > 0.0 || fHeader.bounds.top > 0.0)
151 					DEBUGGER("non-origin bounds!");
152 				fBitmap = new (std::nothrow )BBitmap(fHeader.bounds,
153 					0, fHeader.colors, fHeader.rowBytes);
154 				if (fBitmap == NULL)
155 					return B_ERROR;
156 				if (!fBitmap->IsValid()) {
157 					status_t error = fBitmap->InitCheck();
158 					delete fBitmap;
159 					fBitmap = NULL;
160 					return error;
161 				}
162 				if ((uint32)fBitmap->BytesPerRow() != fHeader.rowBytes) {
163 					fprintf(stderr, "BitmapStream BytesPerRow width %" B_PRId32 " does not match "
164 						"value declared in header %" B_PRId32 "\n",
165 						fBitmap->BytesPerRow(), fHeader.rowBytes);
166 					return B_MISMATCHED_VALUES;
167 				}
168 			}
169 			if (fBitmap != NULL)
170 				fSize = sizeof(TranslatorBitmap) + fBitmap->BitsLength();
171 		}
172 	}
173 	return written;
174 }
175 
176 
177 // Changes the current stream position.
178 off_t
179 BBitmapStream::Seek(off_t position, uint32 seekMode)
180 {
181 	// When whence == SEEK_SET, it just falls through to
182 	// fPosition = position
183 	if (seekMode == SEEK_CUR)
184 		position += fPosition;
185 	else if (seekMode == SEEK_END)
186 		position += fSize;
187 
188 	if (position < 0 || position > (off_t)fSize)
189 		return B_BAD_VALUE;
190 
191 	fPosition = position;
192 	return fPosition;
193 }
194 
195 
196 // Returns the current stream position
197 off_t
198 BBitmapStream::Position() const
199 {
200 	return fPosition;
201 }
202 
203 
204 
205 // Returns the current stream size
206 off_t
207 BBitmapStream::Size() const
208 {
209 	return fSize;
210 }
211 
212 
213 // Sets the size of the data.
214 // I'm not sure if this method has any real purpose.
215 status_t
216 BBitmapStream::SetSize(off_t size)
217 {
218 	if (size < 0)
219 		return B_BAD_VALUE;
220 	if (fBitmap && (size > (off_t)(fHeader.dataSize + sizeof(TranslatorBitmap))))
221 		return B_BAD_VALUE;
222 	// Problem:
223 	// What if someone calls SetSize() before writing the header,
224 	// so we don't know what bitmap to create?
225 	// Solution:
226 	// We assume people will write the header before any data,
227 	// so SetSize() is really not going to do anything.
228 	if (fBitmap != NULL)
229 		fSize = size;
230 
231 	return B_NO_ERROR;
232 }
233 
234 
235 // Sets _bitmap to point to the internal bitmap object.
236 status_t
237 BBitmapStream::DetachBitmap(BBitmap** _bitmap)
238 {
239 	if (_bitmap == NULL)
240 		return B_BAD_VALUE;
241 	if (!fBitmap || fDetached)
242 		return B_ERROR;
243 
244 	fDetached = true;
245 	*_bitmap = fBitmap;
246 
247 	return B_OK;
248 }
249 
250 
251 // Swaps the byte order of source, no matter the byte order, and
252 // copies the result to destination.
253 void
254 BBitmapStream::SwapHeader(const TranslatorBitmap* source,
255 	TranslatorBitmap* destination)
256 {
257 	if (source == NULL || destination == NULL)
258 		return;
259 
260 	*destination = *source;
261 	swap_data(B_UINT32_TYPE, destination, sizeof(TranslatorBitmap),
262 		B_SWAP_ALWAYS);
263 }
264 
265 
266 void BBitmapStream::_ReservedBitmapStream1() {}
267 void BBitmapStream::_ReservedBitmapStream2() {}
268