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.
24BBitmapStream::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
55BBitmapStream::~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.
65ssize_t
66BBitmapStream::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.
95ssize_t
96BBitmapStream::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.
178off_t
179BBitmapStream::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
197off_t
198BBitmapStream::Position() const
199{
200	return fPosition;
201}
202
203
204
205// Returns the current stream size
206off_t
207BBitmapStream::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.
215status_t
216BBitmapStream::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.
236status_t
237BBitmapStream::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.
253void
254BBitmapStream::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
266void BBitmapStream::_ReservedBitmapStream1() {}
267void BBitmapStream::_ReservedBitmapStream2() {}
268