1 | /* |
2 | * Copyright 2016 Google Inc. |
3 | * |
4 | * Use of this source code is governed by a BSD-style license that can be |
5 | * found in the LICENSE file. |
6 | */ |
7 | |
8 | #ifndef SkStreamBuffer_DEFINED |
9 | #define SkStreamBuffer_DEFINED |
10 | |
11 | #include "include/core/SkData.h" |
12 | #include "include/core/SkStream.h" |
13 | #include "include/core/SkTypes.h" |
14 | #include "include/private/SkTHash.h" |
15 | |
16 | /** |
17 | * Helper class for reading from a stream that may not have all its data |
18 | * available yet. |
19 | * |
20 | * Used by GIFImageReader, and currently set up for that use case. |
21 | * |
22 | * Buffers up to 256 * 3 bytes (256 colors, with 3 bytes each) to support GIF. |
23 | * FIXME (scroggo): Make this more general purpose? |
24 | */ |
25 | class SkStreamBuffer : SkNoncopyable { |
26 | public: |
27 | SkStreamBuffer(std::unique_ptr<SkStream>); |
28 | ~SkStreamBuffer(); |
29 | |
30 | /** |
31 | * Return a pointer the buffered data. |
32 | * |
33 | * The number of bytes buffered is the number passed to buffer() |
34 | * after the last call to flush(). |
35 | */ |
36 | const char* get() const; |
37 | |
38 | /** |
39 | * Buffer from the stream into our buffer. |
40 | * |
41 | * If this call returns true, get() can be used to access |bytes| bytes |
42 | * from the stream. In addition, markPosition() can be called to mark this |
43 | * position and enable calling getAtPosition() later to retrieve |bytes| |
44 | * bytes. |
45 | * |
46 | * @param bytes Total number of bytes desired. |
47 | * |
48 | * @return Whether all bytes were successfully buffered. |
49 | */ |
50 | bool buffer(size_t bytes); |
51 | |
52 | /** |
53 | * Flush the buffer. |
54 | * |
55 | * After this call, no bytes are buffered. |
56 | */ |
57 | void flush() { |
58 | if (fHasLengthAndPosition) { |
59 | if (fTrulyBuffered < fBytesBuffered) { |
60 | fStream->move(fBytesBuffered - fTrulyBuffered); |
61 | } |
62 | fTrulyBuffered = 0; |
63 | } |
64 | fPosition += fBytesBuffered; |
65 | fBytesBuffered = 0; |
66 | } |
67 | |
68 | /** |
69 | * Mark the current position in the stream to return to it later. |
70 | * |
71 | * This is the position of the start of the buffer. After this call, a |
72 | * a client can call getDataAtPosition to retrieve all the bytes currently |
73 | * buffered. |
74 | * |
75 | * @return size_t Position which can be passed to getDataAtPosition later |
76 | * to retrieve the data currently buffered. |
77 | */ |
78 | size_t markPosition(); |
79 | |
80 | /** |
81 | * Retrieve data at position, as previously marked by markPosition(). |
82 | * |
83 | * @param position Position to retrieve data, as marked by markPosition(). |
84 | * @param length Amount of data required at position. |
85 | * @return SkData The data at position. |
86 | */ |
87 | sk_sp<SkData> getDataAtPosition(size_t position, size_t length); |
88 | |
89 | private: |
90 | static constexpr size_t kMaxSize = 256 * 3; |
91 | |
92 | std::unique_ptr<SkStream> fStream; |
93 | size_t fPosition; |
94 | char fBuffer[kMaxSize]; |
95 | size_t fBytesBuffered; |
96 | // If the stream has a length and position, we can make two optimizations: |
97 | // - We can skip buffering |
98 | // - During parsing, we can store the position and size of data that is |
99 | // needed later during decoding. |
100 | const bool fHasLengthAndPosition; |
101 | // When fHasLengthAndPosition is true, we do not need to actually buffer |
102 | // inside buffer(). We'll buffer inside get(). This keeps track of how many |
103 | // bytes we've buffered inside get(), for the (non-existent) case of: |
104 | // buffer(n) |
105 | // get() |
106 | // buffer(n + u) |
107 | // get() |
108 | // The second call to get() needs to only truly buffer the part that was |
109 | // not already buffered. |
110 | mutable size_t fTrulyBuffered; |
111 | // Only used if !fHasLengthAndPosition. In that case, markPosition will |
112 | // copy into an SkData, stored here. |
113 | SkTHashMap<size_t, SkData*> fMarkedData; |
114 | }; |
115 | #endif // SkStreamBuffer_DEFINED |
116 | |
117 | |