OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "nacl_app/png_loader.h" | |
6 | |
7 using url_io::WebResourceLoader; | |
8 | |
9 namespace { | |
10 static const size_t kPngSignatureLength = 8; | |
11 | |
12 const uint32_t kRedBlueMask = 0x00FF00FF; | |
13 const uint32_t kGreenMask = 0x0000FF00; | |
14 const uint32_t kAlphaMask = 0xFF000000; | |
15 const uint32_t kPixelOne = 0xFF; | |
16 const uint32_t kAlphaShift = 24; | |
17 } // namespace | |
18 | |
19 namespace flocking_geese { | |
20 | |
21 // Wrapper function handed to libpng that reads the PNG data. | |
22 void ReadDataFromInputStream(png_structp png_ptr, | |
23 png_bytep out_bytes, | |
24 png_size_t byte_count) { | |
25 if(png_ptr->io_ptr == NULL) | |
26 return; | |
27 flocking_geese::PngLoader* png_loader = | |
28 static_cast<flocking_geese::PngLoader*>(png_ptr->io_ptr); | |
29 const size_t bytes_read = png_loader->ReadPngData( | |
30 static_cast<uint8_t*>(out_bytes), | |
31 static_cast<size_t>(byte_count)); | |
32 | |
33 if (static_cast<png_size_t>(bytes_read) != byte_count) | |
34 return; | |
35 } | |
36 | |
37 PngLoader::~PngLoader() { | |
38 ReleaseAndInvalidate(); | |
39 } | |
40 | |
41 void PngLoader::ReleaseAndInvalidate() { | |
42 is_valid_ = false; | |
43 if (png_ptr_ && png_info_ptr_) { | |
44 png_destroy_read_struct(&png_ptr_, &png_info_ptr_, NULL); | |
45 } else if (png_ptr_) { | |
46 png_destroy_read_struct(&png_ptr_, NULL, NULL); | |
47 } | |
48 png_ptr_ = NULL; | |
49 png_info_ptr_ = NULL; | |
50 png_image_size_.SetSize(0, 0); | |
51 } | |
52 | |
53 void PngLoader::OnLoaderReceivedResponseInfo(WebResourceLoader* loader) { | |
54 ReleaseAndInvalidate(); // Start with clean data. | |
55 // The content length should be a value greater than 0 or -1 for a | |
56 // continuous stream. | |
57 content_length_ = loader->GetContentLength(); | |
58 assert(content_length_ == -1 or content_length_ > 0); | |
59 if (content_length_ == 0 || content_length_ < -1) { | |
60 return; | |
61 } | |
62 // Allocate the internal buffer that will hold all the PNG data. Start | |
63 // reading data into the beginning of this buffer. | |
64 png_data_.reset(new uint8_t[content_length_]); | |
65 url_bytes_read_ = 0; | |
66 loader->set_content_buffer(png_data_.get(), content_length_); | |
67 loader->ReadMoreData(); | |
68 } | |
69 | |
70 void PngLoader::OnLoaderReceivedData(WebResourceLoader* loader) { | |
71 // Advance the internal bufer pointer to the end of the current data and | |
72 // issue another read. | |
73 url_bytes_read_ += loader->data_size(); | |
74 if (url_bytes_read_ < content_length_) { | |
75 uint8_t* buffer_ptr = &(png_data_[url_bytes_read_]); | |
76 loader->set_content_buffer(buffer_ptr, content_length_); | |
77 } else { | |
78 // This will cause the loader to switch to an internal buffer and protect | |
79 // |png_data_| from possibn=le overruns. | |
80 loader->set_content_buffer(NULL, 0); | |
81 } | |
82 loader->ReadMoreData(); | |
83 } | |
84 | |
85 void PngLoader::OnLoaderCompletedDownload(WebResourceLoader* loader) { | |
86 url_bytes_read_ = 0; | |
87 is_valid_ = true; | |
88 png_data_pos_ = 0; | |
89 // Validate the data and initialize the PNG structs. | |
90 uint8_t png_signature[kPngSignatureLength]; | |
91 if (ReadPngData(png_signature, kPngSignatureLength) != kPngSignatureLength) { | |
92 ReleaseAndInvalidate(); | |
93 return; | |
94 } | |
95 if (!png_check_sig(png_signature, kPngSignatureLength)) { | |
96 ReleaseAndInvalidate(); | |
97 return; | |
98 } | |
99 png_ptr_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
100 if (png_ptr_ == NULL) { | |
101 ReleaseAndInvalidate(); | |
102 return; | |
103 } | |
104 png_info_ptr_ = png_create_info_struct(png_ptr_); | |
105 if (png_info_ptr_ == NULL) { | |
106 ReleaseAndInvalidate(); | |
107 return; | |
108 } | |
109 png_set_read_fn(png_ptr_, this, ReadDataFromInputStream); | |
110 // Tell libpng that the signature bytes have been read. | |
111 png_set_sig_bytes(png_ptr_, kPngSignatureLength); | |
112 | |
113 // Try to read and validate the header info. | |
114 png_read_info(png_ptr_, png_info_ptr_); | |
115 png_uint_32 width = 0; | |
116 png_uint_32 height = 0; | |
117 int bits_per_sample; | |
118 int pixel_format; | |
119 png_uint_32 png_error = png_get_IHDR( | |
120 png_ptr_, | |
121 png_info_ptr_, | |
122 &width, | |
123 &height, | |
124 &bits_per_sample, | |
125 &pixel_format, | |
126 NULL, NULL, NULL); | |
127 if (png_error != 1) { | |
128 ReleaseAndInvalidate(); | |
129 return; | |
130 } | |
131 png_image_size_.SetSize(width, height); | |
132 // Tell libpng to strip 16 bit/color files down to 8 bits/color. | |
133 png_set_strip_16(png_ptr_); | |
134 switch (pixel_format) { | |
135 case PNG_COLOR_TYPE_PALETTE: | |
136 // Expand paletted colors into true RGB triplets. | |
137 png_set_palette_to_rgb(png_ptr_); | |
138 break; | |
139 case PNG_COLOR_TYPE_GRAY: | |
140 // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. | |
141 if (bits_per_sample < 8) | |
142 png_set_expand_gray_1_2_4_to_8(png_ptr_); | |
143 break; | |
144 case PNG_COLOR_TYPE_RGB: | |
145 png_set_expand(png_ptr_); | |
146 break; | |
147 } | |
148 // Expand paletted or RGB images with transparency to full alpha channels | |
149 // so the data will be available as RGBA quartets. | |
150 if (png_get_valid(png_ptr_, png_info_ptr_, PNG_INFO_tRNS)) | |
151 png_set_tRNS_to_alpha(png_ptr_); | |
152 // Change RGBA -> ARGB. | |
153 // png_set_swap_alpha(png_ptr_); | |
154 } | |
155 | |
156 void PngLoader::OnLoaderError(int32_t error, WebResourceLoader* loader) { | |
157 ReleaseAndInvalidate(); | |
158 } | |
159 | |
160 void PngLoader::OnLoaderDone(WebResourceLoader* loader) { | |
161 loader->CloseAndDeleteSelf(); | |
162 } | |
163 | |
164 void PngLoader::FillPixelBuffer(uint32_t* pixel_buffer) { | |
165 if (!is_valid()) | |
166 return; | |
167 png_bytep row_pointers[png_image_size_.height()]; | |
168 size_t row_bytes = png_get_rowbytes(png_ptr_, png_info_ptr_); | |
169 for (int32_t row = 0; row < png_image_size_.height(); ++row) { | |
170 row_pointers[row] = static_cast<png_bytep>(png_malloc(png_ptr_, row_bytes)); | |
171 } | |
172 png_read_image(png_ptr_, row_pointers); | |
173 png_read_end(png_ptr_, png_info_ptr_); | |
174 // Copy the PNG data into the given buffer. | |
175 for (int32_t row = 0; row < png_image_size_.height(); ++row) { | |
176 memcpy(pixel_buffer, row_pointers[row], row_bytes); | |
177 PreMultiplyAlpha(pixel_buffer, png_image_size_.width()); | |
178 pixel_buffer += png_image_size_.width(); | |
179 png_free(png_ptr_, row_pointers[row]); | |
180 } | |
181 } | |
182 | |
183 void PngLoader::PreMultiplyAlpha(uint32_t* scanline, int32_t line_width) { | |
184 for (int32_t x = 0; x < line_width; ++x) { | |
185 uint32_t src = *scanline; | |
186 uint32_t rb = src & kRedBlueMask; | |
187 uint32_t g = src & kGreenMask; | |
188 uint32_t alpha = (src >> kAlphaShift) & kPixelOne; | |
189 rb *= alpha; | |
190 g *= alpha; | |
191 rb = (rb >> 8) & kRedBlueMask; | |
192 g = (g >> 8) & kGreenMask; | |
193 *scanline++ = (rb | g | (src & kAlphaMask)); | |
194 } | |
195 } | |
196 | |
197 size_t PngLoader::ReadPngData(uint8_t* buffer, const size_t byte_count) { | |
198 size_t copy_byte_count = byte_count; | |
199 if (png_data_pos_ + byte_count >= static_cast<size_t>(content_length_)) | |
200 copy_byte_count = content_length_ - png_data_pos_; // "end-of-file". | |
201 memcpy(buffer, &(png_data_[png_data_pos_]), copy_byte_count); | |
202 png_data_pos_ += copy_byte_count; | |
203 return copy_byte_count; | |
204 } | |
205 | |
206 } // namespace flocking_geese | |
207 | |
OLD | NEW |