1 |
|
2 |
|
3 | #include "Image.h"
|
4 |
|
5 | #include "bmp/BMPParser.h"
|
6 | #include "Canvas.h"
|
7 | #include <cerrno>
|
8 | #include <cstdlib>
|
9 | #include <cstring>
|
10 | #include <node_buffer.h>
|
11 | #include "Util.h"
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | static constexpr int canvas_max_side = (1 << 15) - 1;
|
17 |
|
18 | #ifdef HAVE_GIF
|
19 | typedef struct {
|
20 | uint8_t *buf;
|
21 | unsigned len;
|
22 | unsigned pos;
|
23 | } gif_data_t;
|
24 | #endif
|
25 |
|
26 | #ifdef HAVE_JPEG
|
27 | #include <csetjmp>
|
28 |
|
29 | struct canvas_jpeg_error_mgr: jpeg_error_mgr {
|
30 | Image* image;
|
31 | jmp_buf setjmp_buffer;
|
32 | };
|
33 | #endif
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | typedef struct {
|
40 | unsigned len;
|
41 | uint8_t *buf;
|
42 | } read_closure_t;
|
43 |
|
44 | using namespace v8;
|
45 |
|
46 | Nan::Persistent<FunctionTemplate> Image::constructor;
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 | void
|
53 | Image::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
|
54 | Nan::HandleScope scope;
|
55 |
|
56 | Local<FunctionTemplate> ctor = Nan::New<FunctionTemplate>(Image::New);
|
57 | constructor.Reset(ctor);
|
58 | ctor->InstanceTemplate()->SetInternalFieldCount(1);
|
59 | ctor->SetClassName(Nan::New("Image").ToLocalChecked());
|
60 |
|
61 |
|
62 | Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
|
63 | SetProtoAccessor(proto, Nan::New("complete").ToLocalChecked(), GetComplete, NULL, ctor);
|
64 | SetProtoAccessor(proto, Nan::New("width").ToLocalChecked(), GetWidth, SetWidth, ctor);
|
65 | SetProtoAccessor(proto, Nan::New("height").ToLocalChecked(), GetHeight, SetHeight, ctor);
|
66 | SetProtoAccessor(proto, Nan::New("naturalWidth").ToLocalChecked(), GetNaturalWidth, NULL, ctor);
|
67 | SetProtoAccessor(proto, Nan::New("naturalHeight").ToLocalChecked(), GetNaturalHeight, NULL, ctor);
|
68 | SetProtoAccessor(proto, Nan::New("dataMode").ToLocalChecked(), GetDataMode, SetDataMode, ctor);
|
69 |
|
70 | ctor->Set(Nan::New("MODE_IMAGE").ToLocalChecked(), Nan::New<Number>(DATA_IMAGE));
|
71 | ctor->Set(Nan::New("MODE_MIME").ToLocalChecked(), Nan::New<Number>(DATA_MIME));
|
72 |
|
73 | Local<Context> ctx = Nan::GetCurrentContext();
|
74 | Nan::Set(target, Nan::New("Image").ToLocalChecked(), ctor->GetFunction(ctx).ToLocalChecked());
|
75 |
|
76 |
|
77 | NAN_EXPORT(target, GetSource);
|
78 | NAN_EXPORT(target, SetSource);
|
79 | }
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 | NAN_METHOD(Image::New) {
|
86 | if (!info.IsConstructCall()) {
|
87 | return Nan::ThrowTypeError("Class constructors cannot be invoked without 'new'");
|
88 | }
|
89 |
|
90 | Image *img = new Image;
|
91 | img->data_mode = DATA_IMAGE;
|
92 | img->Wrap(info.This());
|
93 | Nan::Set(info.This(), Nan::New("onload").ToLocalChecked(), Nan::Null()).Check();
|
94 | Nan::Set(info.This(), Nan::New("onerror").ToLocalChecked(), Nan::Null()).Check();
|
95 | info.GetReturnValue().Set(info.This());
|
96 | }
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 | NAN_GETTER(Image::GetComplete) {
|
103 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
104 | info.GetReturnValue().Set(Nan::New<Boolean>(Image::COMPLETE == img->state));
|
105 | }
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | NAN_GETTER(Image::GetDataMode) {
|
112 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
113 | info.GetReturnValue().Set(Nan::New<Number>(img->data_mode));
|
114 | }
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | NAN_SETTER(Image::SetDataMode) {
|
121 | if (value->IsNumber()) {
|
122 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
123 | int mode = Nan::To<uint32_t>(value).FromMaybe(0);
|
124 | img->data_mode = (data_mode_t) mode;
|
125 | }
|
126 | }
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | NAN_GETTER(Image::GetNaturalWidth) {
|
133 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
134 | info.GetReturnValue().Set(Nan::New<Number>(img->naturalWidth));
|
135 | }
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 | NAN_GETTER(Image::GetWidth) {
|
142 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
143 | info.GetReturnValue().Set(Nan::New<Number>(img->width));
|
144 | }
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 | NAN_SETTER(Image::SetWidth) {
|
151 | if (value->IsNumber()) {
|
152 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
153 | img->width = Nan::To<uint32_t>(value).FromMaybe(0);
|
154 | }
|
155 | }
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | NAN_GETTER(Image::GetNaturalHeight) {
|
162 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
163 | info.GetReturnValue().Set(Nan::New<Number>(img->naturalHeight));
|
164 | }
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | NAN_GETTER(Image::GetHeight) {
|
171 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
172 | info.GetReturnValue().Set(Nan::New<Number>(img->height));
|
173 | }
|
174 |
|
175 |
|
176 |
|
177 |
|
178 | NAN_SETTER(Image::SetHeight) {
|
179 | if (value->IsNumber()) {
|
180 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
181 | img->height = Nan::To<uint32_t>(value).FromMaybe(0);
|
182 | }
|
183 | }
|
184 |
|
185 |
|
186 |
|
187 |
|
188 |
|
189 | NAN_METHOD(Image::GetSource){
|
190 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
191 | info.GetReturnValue().Set(Nan::New<String>(img->filename ? img->filename : "").ToLocalChecked());
|
192 | }
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 | void
|
199 | Image::clearData() {
|
200 | if (_surface) {
|
201 | cairo_surface_destroy(_surface);
|
202 | Nan::AdjustExternalMemory(-_data_len);
|
203 | _data_len = 0;
|
204 | _surface = NULL;
|
205 | }
|
206 |
|
207 | delete[] _data;
|
208 | _data = nullptr;
|
209 |
|
210 | free(filename);
|
211 | filename = NULL;
|
212 |
|
213 | #ifdef HAVE_RSVG
|
214 | if (_rsvg != NULL) {
|
215 | g_object_unref(_rsvg);
|
216 | _rsvg = NULL;
|
217 | }
|
218 | #endif
|
219 |
|
220 | width = height = 0;
|
221 | naturalWidth = naturalHeight = 0;
|
222 | state = DEFAULT;
|
223 | }
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 | NAN_METHOD(Image::SetSource){
|
230 | Image *img = Nan::ObjectWrap::Unwrap<Image>(info.This());
|
231 | cairo_status_t status = CAIRO_STATUS_READ_ERROR;
|
232 |
|
233 | Local<Value> value = info[0];
|
234 |
|
235 | img->clearData();
|
236 |
|
237 | errno = 0;
|
238 |
|
239 |
|
240 | if (value->IsString()) {
|
241 | Nan::Utf8String src(value);
|
242 | if (img->filename) free(img->filename);
|
243 | img->filename = strdup(*src);
|
244 | status = img->load();
|
245 |
|
246 | } else if (node::Buffer::HasInstance(value)) {
|
247 | uint8_t *buf = (uint8_t *) node::Buffer::Data(Nan::To<Object>(value).ToLocalChecked());
|
248 | unsigned len = node::Buffer::Length(Nan::To<Object>(value).ToLocalChecked());
|
249 | status = img->loadFromBuffer(buf, len);
|
250 | }
|
251 |
|
252 | if (status) {
|
253 | Local<Value> onerrorFn = Nan::Get(info.This(), Nan::New("onerror").ToLocalChecked()).ToLocalChecked();
|
254 | if (onerrorFn->IsFunction()) {
|
255 | Local<Value> argv[1];
|
256 | CanvasError errorInfo = img->errorInfo;
|
257 | if (errorInfo.cerrno) {
|
258 | argv[0] = Nan::ErrnoException(errorInfo.cerrno, errorInfo.syscall.c_str(), errorInfo.message.c_str(), errorInfo.path.c_str());
|
259 | } else if (!errorInfo.message.empty()) {
|
260 | argv[0] = Nan::Error(Nan::New(errorInfo.message).ToLocalChecked());
|
261 | } else {
|
262 | argv[0] = Nan::Error(Nan::New(cairo_status_to_string(status)).ToLocalChecked());
|
263 | }
|
264 | Local<Context> ctx = Nan::GetCurrentContext();
|
265 | Nan::Call(onerrorFn.As<Function>(), ctx->Global(), 1, argv).ToLocalChecked();
|
266 | }
|
267 | } else {
|
268 | img->loaded();
|
269 | Local<Value> onloadFn = Nan::Get(info.This(), Nan::New("onload").ToLocalChecked()).ToLocalChecked();
|
270 | if (onloadFn->IsFunction()) {
|
271 | Local<Context> ctx = Nan::GetCurrentContext();
|
272 | Nan::Call(onloadFn.As<Function>(), ctx->Global(), 0, NULL).ToLocalChecked();
|
273 | }
|
274 | }
|
275 | }
|
276 |
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 | cairo_status_t
|
283 | Image::loadFromBuffer(uint8_t *buf, unsigned len) {
|
284 | uint8_t data[4] = {0};
|
285 | memcpy(data, buf, (len < 4 ? len : 4) * sizeof(uint8_t));
|
286 |
|
287 | if (isPNG(data)) return loadPNGFromBuffer(buf);
|
288 |
|
289 | if (isGIF(data)) {
|
290 | #ifdef HAVE_GIF
|
291 | return loadGIFFromBuffer(buf, len);
|
292 | #else
|
293 | this->errorInfo.set("node-canvas was built without GIF support");
|
294 | return CAIRO_STATUS_READ_ERROR;
|
295 | #endif
|
296 | }
|
297 |
|
298 | if (isJPEG(data)) {
|
299 | #ifdef HAVE_JPEG
|
300 | if (DATA_IMAGE == data_mode) return loadJPEGFromBuffer(buf, len);
|
301 | if (DATA_MIME == data_mode) return decodeJPEGBufferIntoMimeSurface(buf, len);
|
302 | if ((DATA_IMAGE | DATA_MIME) == data_mode) {
|
303 | cairo_status_t status;
|
304 | status = loadJPEGFromBuffer(buf, len);
|
305 | if (status) return status;
|
306 | return assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
|
307 | }
|
308 | #else
|
309 | this->errorInfo.set("node-canvas was built without JPEG support");
|
310 | return CAIRO_STATUS_READ_ERROR;
|
311 | #endif
|
312 | }
|
313 |
|
314 |
|
315 |
|
316 | unsigned head_len = (len < 1000 ? len : 1000);
|
317 | if (isSVG(buf, head_len)) {
|
318 | #ifdef HAVE_RSVG
|
319 | return loadSVGFromBuffer(buf, len);
|
320 | #else
|
321 | this->errorInfo.set("node-canvas was built without SVG support");
|
322 | return CAIRO_STATUS_READ_ERROR;
|
323 | #endif
|
324 | }
|
325 |
|
326 | if (isBMP(buf, len))
|
327 | return loadBMPFromBuffer(buf, len);
|
328 |
|
329 | this->errorInfo.set("Unsupported image type");
|
330 | return CAIRO_STATUS_READ_ERROR;
|
331 | }
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 | cairo_status_t
|
338 | Image::loadPNGFromBuffer(uint8_t *buf) {
|
339 | read_closure_t closure;
|
340 | closure.len = 0;
|
341 | closure.buf = buf;
|
342 | _surface = cairo_image_surface_create_from_png_stream(readPNG, &closure);
|
343 | cairo_status_t status = cairo_surface_status(_surface);
|
344 | if (status) return status;
|
345 | return CAIRO_STATUS_SUCCESS;
|
346 | }
|
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 | cairo_status_t
|
353 | Image::readPNG(void *c, uint8_t *data, unsigned int len) {
|
354 | read_closure_t *closure = (read_closure_t *) c;
|
355 | memcpy(data, closure->buf + closure->len, len);
|
356 | closure->len += len;
|
357 | return CAIRO_STATUS_SUCCESS;
|
358 | }
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 | Image::Image() {
|
365 | filename = NULL;
|
366 | _data = nullptr;
|
367 | _data_len = 0;
|
368 | _surface = NULL;
|
369 | width = height = 0;
|
370 | naturalWidth = naturalHeight = 0;
|
371 | state = DEFAULT;
|
372 | #ifdef HAVE_RSVG
|
373 | _rsvg = NULL;
|
374 | _is_svg = false;
|
375 | _svg_last_width = _svg_last_height = 0;
|
376 | #endif
|
377 | }
|
378 |
|
379 |
|
380 |
|
381 |
|
382 |
|
383 | Image::~Image() {
|
384 | clearData();
|
385 | }
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 | cairo_status_t
|
392 | Image::load() {
|
393 | if (LOADING != state) {
|
394 | state = LOADING;
|
395 | return loadSurface();
|
396 | }
|
397 | return CAIRO_STATUS_READ_ERROR;
|
398 | }
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 | void
|
405 | Image::loaded() {
|
406 | Nan::HandleScope scope;
|
407 | state = COMPLETE;
|
408 |
|
409 | width = naturalWidth = cairo_image_surface_get_width(_surface);
|
410 | height = naturalHeight = cairo_image_surface_get_height(_surface);
|
411 | _data_len = naturalHeight * cairo_image_surface_get_stride(_surface);
|
412 | Nan::AdjustExternalMemory(_data_len);
|
413 | }
|
414 |
|
415 |
|
416 |
|
417 |
|
418 | cairo_surface_t *Image::surface() {
|
419 | #ifdef HAVE_RSVG
|
420 | if (_is_svg && (_svg_last_width != width || _svg_last_height != height)) {
|
421 | if (_surface != NULL) {
|
422 | cairo_surface_destroy(_surface);
|
423 | _surface = NULL;
|
424 | }
|
425 |
|
426 | cairo_status_t status = renderSVGToSurface();
|
427 | if (status != CAIRO_STATUS_SUCCESS) {
|
428 | g_object_unref(_rsvg);
|
429 | Nan::ThrowError(Canvas::Error(status));
|
430 | return NULL;
|
431 | }
|
432 | }
|
433 | #endif
|
434 | return _surface;
|
435 | }
|
436 |
|
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 |
|
443 |
|
444 | cairo_status_t
|
445 | Image::loadSurface() {
|
446 | FILE *stream = fopen(filename, "rb");
|
447 | if (!stream) {
|
448 | this->errorInfo.set(NULL, "fopen", errno, filename);
|
449 | return CAIRO_STATUS_READ_ERROR;
|
450 | }
|
451 | uint8_t buf[5];
|
452 | if (1 != fread(&buf, 5, 1, stream)) {
|
453 | fclose(stream);
|
454 | return CAIRO_STATUS_READ_ERROR;
|
455 | }
|
456 | rewind(stream);
|
457 |
|
458 |
|
459 | if (isPNG(buf)) {
|
460 | fclose(stream);
|
461 | return loadPNG();
|
462 | }
|
463 |
|
464 |
|
465 | if (isGIF(buf)) {
|
466 | #ifdef HAVE_GIF
|
467 | return loadGIF(stream);
|
468 | #else
|
469 | this->errorInfo.set("node-canvas was built without GIF support");
|
470 | return CAIRO_STATUS_READ_ERROR;
|
471 | #endif
|
472 | }
|
473 |
|
474 | if (isJPEG(buf)) {
|
475 | #ifdef HAVE_JPEG
|
476 | return loadJPEG(stream);
|
477 | #else
|
478 | this->errorInfo.set("node-canvas was built without JPEG support");
|
479 | return CAIRO_STATUS_READ_ERROR;
|
480 | #endif
|
481 | }
|
482 |
|
483 |
|
484 |
|
485 | uint8_t head[1000] = {0};
|
486 | fseek(stream, 0 , SEEK_END);
|
487 | long len = ftell(stream);
|
488 | unsigned head_len = (len < 1000 ? len : 1000);
|
489 | unsigned head_size = head_len * sizeof(uint8_t);
|
490 | rewind(stream);
|
491 | if (head_size != fread(&head, 1, head_size, stream)) {
|
492 | fclose(stream);
|
493 | return CAIRO_STATUS_READ_ERROR;
|
494 | }
|
495 | rewind(stream);
|
496 | if (isSVG(head, head_len)) {
|
497 | #ifdef HAVE_RSVG
|
498 | return loadSVG(stream);
|
499 | #else
|
500 | this->errorInfo.set("node-canvas was built without SVG support");
|
501 | return CAIRO_STATUS_READ_ERROR;
|
502 | #endif
|
503 | }
|
504 |
|
505 | if (isBMP(buf, 2))
|
506 | return loadBMP(stream);
|
507 |
|
508 | fclose(stream);
|
509 |
|
510 | this->errorInfo.set("Unsupported image type");
|
511 | return CAIRO_STATUS_READ_ERROR;
|
512 | }
|
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 | cairo_status_t
|
519 | Image::loadPNG() {
|
520 | _surface = cairo_image_surface_create_from_png(filename);
|
521 | return cairo_surface_status(_surface);
|
522 | }
|
523 |
|
524 |
|
525 |
|
526 | #ifdef HAVE_GIF
|
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 | int
|
533 | get_gif_transparent_color(GifFileType *gif, int frame) {
|
534 | ExtensionBlock *ext = gif->SavedImages[frame].ExtensionBlocks;
|
535 | int len = gif->SavedImages[frame].ExtensionBlockCount;
|
536 | for (int x = 0; x < len; ++x, ++ext) {
|
537 | if ((ext->Function == GRAPHICS_EXT_FUNC_CODE) && (ext->Bytes[0] & 1)) {
|
538 | return ext->Bytes[3] == 0 ? 0 : (uint8_t) ext->Bytes[3];
|
539 | }
|
540 | }
|
541 | return -1;
|
542 | }
|
543 |
|
544 |
|
545 |
|
546 |
|
547 |
|
548 | int
|
549 | read_gif_from_memory(GifFileType *gif, GifByteType *buf, int len) {
|
550 | gif_data_t *data = (gif_data_t *) gif->UserData;
|
551 | if ((data->pos + len) > data->len) len = data->len - data->pos;
|
552 | memcpy(buf, data->pos + data->buf, len);
|
553 | data->pos += len;
|
554 | return len;
|
555 | }
|
556 |
|
557 |
|
558 |
|
559 |
|
560 |
|
561 | cairo_status_t
|
562 | Image::loadGIF(FILE *stream) {
|
563 | struct stat s;
|
564 | int fd = fileno(stream);
|
565 |
|
566 |
|
567 | if (fstat(fd, &s) < 0) {
|
568 | fclose(stream);
|
569 | return CAIRO_STATUS_READ_ERROR;
|
570 | }
|
571 |
|
572 | uint8_t *buf = (uint8_t *) malloc(s.st_size);
|
573 |
|
574 | if (!buf) {
|
575 | fclose(stream);
|
576 | this->errorInfo.set(NULL, "malloc", errno);
|
577 | return CAIRO_STATUS_NO_MEMORY;
|
578 | }
|
579 |
|
580 | size_t read = fread(buf, s.st_size, 1, stream);
|
581 | fclose(stream);
|
582 |
|
583 | cairo_status_t result = CAIRO_STATUS_READ_ERROR;
|
584 | if (1 == read) result = loadGIFFromBuffer(buf, s.st_size);
|
585 | free(buf);
|
586 |
|
587 | return result;
|
588 | }
|
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 | cairo_status_t
|
595 | Image::loadGIFFromBuffer(uint8_t *buf, unsigned len) {
|
596 | int i = 0;
|
597 | GifFileType* gif;
|
598 |
|
599 | gif_data_t gifd = { buf, len, 0 };
|
600 |
|
601 | #if GIFLIB_MAJOR >= 5
|
602 | int errorcode;
|
603 | if ((gif = DGifOpen((void*) &gifd, read_gif_from_memory, &errorcode)) == NULL)
|
604 | return CAIRO_STATUS_READ_ERROR;
|
605 | #else
|
606 | if ((gif = DGifOpen((void*) &gifd, read_gif_from_memory)) == NULL)
|
607 | return CAIRO_STATUS_READ_ERROR;
|
608 | #endif
|
609 |
|
610 | if (GIF_OK != DGifSlurp(gif)) {
|
611 | GIF_CLOSE_FILE(gif);
|
612 | return CAIRO_STATUS_READ_ERROR;
|
613 | }
|
614 |
|
615 | if (gif->SWidth > canvas_max_side || gif->SHeight > canvas_max_side) {
|
616 | GIF_CLOSE_FILE(gif);
|
617 | return CAIRO_STATUS_INVALID_SIZE;
|
618 | }
|
619 |
|
620 | width = naturalWidth = gif->SWidth;
|
621 | height = naturalHeight = gif->SHeight;
|
622 |
|
623 | uint8_t *data = new uint8_t[naturalWidth * naturalHeight * 4];
|
624 | if (!data) {
|
625 | GIF_CLOSE_FILE(gif);
|
626 | this->errorInfo.set(NULL, "malloc", errno);
|
627 | return CAIRO_STATUS_NO_MEMORY;
|
628 | }
|
629 |
|
630 | GifImageDesc *img = &gif->SavedImages[i].ImageDesc;
|
631 |
|
632 |
|
633 | ColorMapObject *colormap = img->ColorMap
|
634 | ? img->ColorMap
|
635 | : gif->SColorMap;
|
636 |
|
637 | if (colormap == nullptr) {
|
638 | GIF_CLOSE_FILE(gif);
|
639 | return CAIRO_STATUS_READ_ERROR;
|
640 | }
|
641 |
|
642 | int bgColor = 0;
|
643 | int alphaColor = get_gif_transparent_color(gif, i);
|
644 | if (gif->SColorMap) bgColor = (uint8_t) gif->SBackGroundColor;
|
645 | else if(alphaColor >= 0) bgColor = alphaColor;
|
646 |
|
647 | uint8_t *src_data = (uint8_t*) gif->SavedImages[i].RasterBits;
|
648 | uint32_t *dst_data = (uint32_t*) data;
|
649 |
|
650 | if (!gif->Image.Interlace) {
|
651 | if (naturalWidth == img->Width && naturalHeight == img->Height) {
|
652 | for (int y = 0; y < naturalHeight; ++y) {
|
653 | for (int x = 0; x < naturalWidth; ++x) {
|
654 | *dst_data = ((*src_data == alphaColor) ? 0 : 255) << 24
|
655 | | colormap->Colors[*src_data].Red << 16
|
656 | | colormap->Colors[*src_data].Green << 8
|
657 | | colormap->Colors[*src_data].Blue;
|
658 |
|
659 | dst_data++;
|
660 | src_data++;
|
661 | }
|
662 | }
|
663 | } else {
|
664 |
|
665 | int bottom = img->Top + img->Height;
|
666 | int right = img->Left + img->Width;
|
667 |
|
668 | uint32_t bgPixel =
|
669 | ((bgColor == alphaColor) ? 0 : 255) << 24
|
670 | | colormap->Colors[bgColor].Red << 16
|
671 | | colormap->Colors[bgColor].Green << 8
|
672 | | colormap->Colors[bgColor].Blue;
|
673 |
|
674 | for (int y = 0; y < naturalHeight; ++y) {
|
675 | for (int x = 0; x < naturalWidth; ++x) {
|
676 | if (y < img->Top || y >= bottom || x < img->Left || x >= right) {
|
677 | *dst_data = bgPixel;
|
678 | dst_data++;
|
679 | } else {
|
680 | *dst_data = ((*src_data == alphaColor) ? 0 : 255) << 24
|
681 | | colormap->Colors[*src_data].Red << 16
|
682 | | colormap->Colors[*src_data].Green << 8
|
683 | | colormap->Colors[*src_data].Blue;
|
684 | dst_data++;
|
685 | src_data++;
|
686 | }
|
687 | }
|
688 | }
|
689 | }
|
690 | } else {
|
691 |
|
692 |
|
693 |
|
694 | int ioffs[] = { 0, 4, 2, 1 };
|
695 | int ijumps[] = { 8, 8, 4, 2 };
|
696 |
|
697 | uint8_t *src_ptr = src_data;
|
698 | uint32_t *dst_ptr;
|
699 |
|
700 | for(int z = 0; z < 4; z++) {
|
701 | for(int y = ioffs[z]; y < naturalHeight; y += ijumps[z]) {
|
702 | dst_ptr = dst_data + naturalWidth * y;
|
703 | for(int x = 0; x < naturalWidth; ++x) {
|
704 | *dst_ptr = ((*src_ptr == alphaColor) ? 0 : 255) << 24
|
705 | | (colormap->Colors[*src_ptr].Red) << 16
|
706 | | (colormap->Colors[*src_ptr].Green) << 8
|
707 | | (colormap->Colors[*src_ptr].Blue);
|
708 |
|
709 | dst_ptr++;
|
710 | src_ptr++;
|
711 | }
|
712 | }
|
713 | }
|
714 | }
|
715 |
|
716 | GIF_CLOSE_FILE(gif);
|
717 |
|
718 |
|
719 | _surface = cairo_image_surface_create_for_data(
|
720 | data
|
721 | , CAIRO_FORMAT_ARGB32
|
722 | , naturalWidth
|
723 | , naturalHeight
|
724 | , cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, naturalWidth));
|
725 |
|
726 | cairo_status_t status = cairo_surface_status(_surface);
|
727 |
|
728 | if (status) {
|
729 | delete[] data;
|
730 | return status;
|
731 | }
|
732 |
|
733 | _data = data;
|
734 |
|
735 | return CAIRO_STATUS_SUCCESS;
|
736 | }
|
737 | #endif
|
738 |
|
739 |
|
740 |
|
741 | #ifdef HAVE_JPEG
|
742 |
|
743 |
|
744 |
|
745 | #if JPEG_LIB_VERSION < 80 && !defined(MEM_SRCDST_SUPPORTED)
|
746 |
|
747 |
|
748 | static void
|
749 | init_source(j_decompress_ptr cinfo) {}
|
750 |
|
751 | static boolean
|
752 | fill_input_buffer(j_decompress_ptr cinfo) {
|
753 | ERREXIT(cinfo, JERR_INPUT_EMPTY);
|
754 | return TRUE;
|
755 | }
|
756 | static void
|
757 | skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
|
758 | struct jpeg_source_mgr* src = (struct jpeg_source_mgr*) cinfo->src;
|
759 | if (num_bytes > 0) {
|
760 | src->next_input_byte += (size_t) num_bytes;
|
761 | src->bytes_in_buffer -= (size_t) num_bytes;
|
762 | }
|
763 | }
|
764 |
|
765 | static void term_source (j_decompress_ptr cinfo) {}
|
766 | static void jpeg_mem_src (j_decompress_ptr cinfo, void* buffer, long nbytes) {
|
767 | struct jpeg_source_mgr* src;
|
768 |
|
769 | if (cinfo->src == NULL) {
|
770 | cinfo->src = (struct jpeg_source_mgr *)
|
771 | (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
|
772 | sizeof(struct jpeg_source_mgr));
|
773 | }
|
774 |
|
775 | src = (struct jpeg_source_mgr*) cinfo->src;
|
776 | src->init_source = init_source;
|
777 | src->fill_input_buffer = fill_input_buffer;
|
778 | src->skip_input_data = skip_input_data;
|
779 | src->resync_to_restart = jpeg_resync_to_restart;
|
780 | src->term_source = term_source;
|
781 | src->bytes_in_buffer = nbytes;
|
782 | src->next_input_byte = (JOCTET*)buffer;
|
783 | }
|
784 |
|
785 | #endif
|
786 |
|
787 | void Image::jpegToARGB(jpeg_decompress_struct* args, uint8_t* data, uint8_t* src, JPEGDecodeL decode) {
|
788 | int stride = naturalWidth * 4;
|
789 | for (int y = 0; y < naturalHeight; ++y) {
|
790 | jpeg_read_scanlines(args, &src, 1);
|
791 | uint32_t *row = (uint32_t*)(data + stride * y);
|
792 | for (int x = 0; x < naturalWidth; ++x) {
|
793 | int bx = args->output_components * x;
|
794 | row[x] = decode(src + bx);
|
795 | }
|
796 | }
|
797 | }
|
798 |
|
799 |
|
800 |
|
801 |
|
802 |
|
803 |
|
804 | cairo_status_t
|
805 | Image::decodeJPEGIntoSurface(jpeg_decompress_struct *args) {
|
806 | cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
807 |
|
808 | uint8_t *data = new uint8_t[naturalWidth * naturalHeight * 4];
|
809 | if (!data) {
|
810 | jpeg_abort_decompress(args);
|
811 | jpeg_destroy_decompress(args);
|
812 | this->errorInfo.set(NULL, "malloc", errno);
|
813 | return CAIRO_STATUS_NO_MEMORY;
|
814 | }
|
815 |
|
816 | uint8_t *src = new uint8_t[naturalWidth * args->output_components];
|
817 | if (!src) {
|
818 | free(data);
|
819 | jpeg_abort_decompress(args);
|
820 | jpeg_destroy_decompress(args);
|
821 | this->errorInfo.set(NULL, "malloc", errno);
|
822 | return CAIRO_STATUS_NO_MEMORY;
|
823 | }
|
824 |
|
825 |
|
826 |
|
827 | switch (args->out_color_space) {
|
828 | case JCS_CMYK:
|
829 | jpegToARGB(args, data, src, [](uint8_t const* src) {
|
830 | uint16_t k = static_cast<uint16_t>(src[3]);
|
831 | uint8_t r = k * src[0] / 255;
|
832 | uint8_t g = k * src[1] / 255;
|
833 | uint8_t b = k * src[2] / 255;
|
834 | return 255 << 24 | r << 16 | g << 8 | b;
|
835 | });
|
836 | break;
|
837 | case JCS_RGB:
|
838 | jpegToARGB(args, data, src, [](uint8_t const* src) {
|
839 | uint8_t r = src[0], g = src[1], b = src[2];
|
840 | return 255 << 24 | r << 16 | g << 8 | b;
|
841 | });
|
842 | break;
|
843 | case JCS_GRAYSCALE:
|
844 | jpegToARGB(args, data, src, [](uint8_t const* src) {
|
845 | uint8_t v = src[0];
|
846 | return 255 << 24 | v << 16 | v << 8 | v;
|
847 | });
|
848 | break;
|
849 | default:
|
850 | this->errorInfo.set("Unsupported JPEG encoding");
|
851 | status = CAIRO_STATUS_READ_ERROR;
|
852 | break;
|
853 | }
|
854 |
|
855 | if (!status) {
|
856 | _surface = cairo_image_surface_create_for_data(
|
857 | data
|
858 | , CAIRO_FORMAT_ARGB32
|
859 | , naturalWidth
|
860 | , naturalHeight
|
861 | , cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, naturalWidth));
|
862 | }
|
863 |
|
864 | jpeg_finish_decompress(args);
|
865 | jpeg_destroy_decompress(args);
|
866 | status = cairo_surface_status(_surface);
|
867 |
|
868 | delete[] src;
|
869 |
|
870 | if (status) {
|
871 | delete[] data;
|
872 | return status;
|
873 | }
|
874 |
|
875 | _data = data;
|
876 |
|
877 | return CAIRO_STATUS_SUCCESS;
|
878 | }
|
879 |
|
880 |
|
881 |
|
882 |
|
883 |
|
884 | static void canvas_jpeg_error_exit(j_common_ptr cinfo) {
|
885 | canvas_jpeg_error_mgr *cjerr = static_cast<canvas_jpeg_error_mgr*>(cinfo->err);
|
886 | cjerr->output_message(cinfo);
|
887 |
|
888 | longjmp(cjerr->setjmp_buffer, 1);
|
889 | }
|
890 |
|
891 |
|
892 | static void canvas_jpeg_output_message(j_common_ptr cinfo) {
|
893 | canvas_jpeg_error_mgr *cjerr = static_cast<canvas_jpeg_error_mgr*>(cinfo->err);
|
894 | char buff[JMSG_LENGTH_MAX];
|
895 | cjerr->format_message(cinfo, buff);
|
896 |
|
897 | cjerr->image->errorInfo.set(buff);
|
898 | }
|
899 |
|
900 |
|
901 |
|
902 |
|
903 |
|
904 |
|
905 | cairo_status_t
|
906 | Image::decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len) {
|
907 |
|
908 |
|
909 | struct jpeg_decompress_struct args;
|
910 | struct canvas_jpeg_error_mgr err;
|
911 |
|
912 | err.image = this;
|
913 | args.err = jpeg_std_error(&err);
|
914 | args.err->error_exit = canvas_jpeg_error_exit;
|
915 | args.err->output_message = canvas_jpeg_output_message;
|
916 |
|
917 |
|
918 | if (setjmp(err.setjmp_buffer)) {
|
919 |
|
920 |
|
921 | jpeg_destroy_decompress(&args);
|
922 | return CAIRO_STATUS_READ_ERROR;
|
923 | }
|
924 |
|
925 | jpeg_create_decompress(&args);
|
926 |
|
927 | jpeg_mem_src(&args, buf, len);
|
928 |
|
929 | jpeg_read_header(&args, 1);
|
930 | jpeg_start_decompress(&args);
|
931 | width = naturalWidth = args.output_width;
|
932 | height = naturalHeight = args.output_height;
|
933 |
|
934 |
|
935 |
|
936 | int buf_size = naturalHeight * cairo_format_stride_for_width(CAIRO_FORMAT_A1, naturalWidth);
|
937 | uint8_t *data = new uint8_t[buf_size];
|
938 | if (!data) {
|
939 | this->errorInfo.set(NULL, "malloc", errno);
|
940 | return CAIRO_STATUS_NO_MEMORY;
|
941 | }
|
942 |
|
943 |
|
944 | _surface = cairo_image_surface_create_for_data(
|
945 | data
|
946 | , CAIRO_FORMAT_A1
|
947 | , naturalWidth
|
948 | , naturalHeight
|
949 | , cairo_format_stride_for_width(CAIRO_FORMAT_A1, naturalWidth));
|
950 |
|
951 |
|
952 | jpeg_abort_decompress(&args);
|
953 | jpeg_destroy_decompress(&args);
|
954 | cairo_status_t status = cairo_surface_status(_surface);
|
955 |
|
956 | if (status) {
|
957 | delete[] data;
|
958 | return status;
|
959 | }
|
960 |
|
961 | _data = data;
|
962 |
|
963 | return assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
|
964 | }
|
965 |
|
966 |
|
967 |
|
968 |
|
969 |
|
970 | void
|
971 | clearMimeData(void *closure) {
|
972 | Nan::AdjustExternalMemory(
|
973 | -static_cast<int>((static_cast<read_closure_t *>(closure)->len)));
|
974 | free(static_cast<read_closure_t *>(closure)->buf);
|
975 | free(closure);
|
976 | }
|
977 |
|
978 |
|
979 |
|
980 |
|
981 |
|
982 |
|
983 |
|
984 | cairo_status_t
|
985 | Image::assignDataAsMime(uint8_t *data, int len, const char *mime_type) {
|
986 | uint8_t *mime_data = (uint8_t *) malloc(len);
|
987 | if (!mime_data) {
|
988 | this->errorInfo.set(NULL, "malloc", errno);
|
989 | return CAIRO_STATUS_NO_MEMORY;
|
990 | }
|
991 |
|
992 | read_closure_t *mime_closure = (read_closure_t *) malloc(sizeof(read_closure_t));
|
993 | if (!mime_closure) {
|
994 | free(mime_data);
|
995 | this->errorInfo.set(NULL, "malloc", errno);
|
996 | return CAIRO_STATUS_NO_MEMORY;
|
997 | }
|
998 |
|
999 | memcpy(mime_data, data, len);
|
1000 |
|
1001 | mime_closure->buf = mime_data;
|
1002 | mime_closure->len = len;
|
1003 |
|
1004 | Nan::AdjustExternalMemory(len);
|
1005 |
|
1006 | return cairo_surface_set_mime_data(_surface
|
1007 | , mime_type
|
1008 | , mime_data
|
1009 | , len
|
1010 | , clearMimeData
|
1011 | , mime_closure);
|
1012 | }
|
1013 |
|
1014 |
|
1015 |
|
1016 |
|
1017 |
|
1018 | cairo_status_t
|
1019 | Image::loadJPEGFromBuffer(uint8_t *buf, unsigned len) {
|
1020 |
|
1021 |
|
1022 | struct jpeg_decompress_struct args;
|
1023 | struct canvas_jpeg_error_mgr err;
|
1024 |
|
1025 | err.image = this;
|
1026 | args.err = jpeg_std_error(&err);
|
1027 | args.err->error_exit = canvas_jpeg_error_exit;
|
1028 | args.err->output_message = canvas_jpeg_output_message;
|
1029 |
|
1030 |
|
1031 | if (setjmp(err.setjmp_buffer)) {
|
1032 |
|
1033 |
|
1034 | jpeg_destroy_decompress(&args);
|
1035 | return CAIRO_STATUS_READ_ERROR;
|
1036 | }
|
1037 |
|
1038 | jpeg_create_decompress(&args);
|
1039 |
|
1040 | jpeg_mem_src(&args, buf, len);
|
1041 |
|
1042 | jpeg_read_header(&args, 1);
|
1043 | jpeg_start_decompress(&args);
|
1044 | width = naturalWidth = args.output_width;
|
1045 | height = naturalHeight = args.output_height;
|
1046 |
|
1047 | return decodeJPEGIntoSurface(&args);
|
1048 | }
|
1049 |
|
1050 |
|
1051 |
|
1052 |
|
1053 |
|
1054 | cairo_status_t
|
1055 | Image::loadJPEG(FILE *stream) {
|
1056 | cairo_status_t status;
|
1057 |
|
1058 | #if defined(_MSC_VER)
|
1059 | if (false) {
|
1060 | #else
|
1061 | if (data_mode == DATA_IMAGE) {
|
1062 | #endif
|
1063 |
|
1064 | struct jpeg_decompress_struct args;
|
1065 | struct canvas_jpeg_error_mgr err;
|
1066 |
|
1067 | err.image = this;
|
1068 | args.err = jpeg_std_error(&err);
|
1069 | args.err->error_exit = canvas_jpeg_error_exit;
|
1070 | args.err->output_message = canvas_jpeg_output_message;
|
1071 |
|
1072 |
|
1073 | if (setjmp(err.setjmp_buffer)) {
|
1074 |
|
1075 |
|
1076 | jpeg_destroy_decompress(&args);
|
1077 | return CAIRO_STATUS_READ_ERROR;
|
1078 | }
|
1079 |
|
1080 | jpeg_create_decompress(&args);
|
1081 |
|
1082 | jpeg_stdio_src(&args, stream);
|
1083 |
|
1084 | jpeg_read_header(&args, 1);
|
1085 | jpeg_start_decompress(&args);
|
1086 |
|
1087 | if (args.output_width > canvas_max_side || args.output_height > canvas_max_side) {
|
1088 | jpeg_destroy_decompress(&args);
|
1089 | return CAIRO_STATUS_INVALID_SIZE;
|
1090 | }
|
1091 |
|
1092 | width = naturalWidth = args.output_width;
|
1093 | height = naturalHeight = args.output_height;
|
1094 |
|
1095 | status = decodeJPEGIntoSurface(&args);
|
1096 | fclose(stream);
|
1097 | } else {
|
1098 | uint8_t *buf;
|
1099 | unsigned len;
|
1100 |
|
1101 | fseek(stream, 0, SEEK_END);
|
1102 | len = ftell(stream);
|
1103 | fseek(stream, 0, SEEK_SET);
|
1104 |
|
1105 | buf = (uint8_t *) malloc(len);
|
1106 | if (!buf) {
|
1107 | this->errorInfo.set(NULL, "malloc", errno);
|
1108 | return CAIRO_STATUS_NO_MEMORY;
|
1109 | }
|
1110 |
|
1111 | if (fread(buf, len, 1, stream) != 1) {
|
1112 | status = CAIRO_STATUS_READ_ERROR;
|
1113 | } else if ((DATA_IMAGE | DATA_MIME) == data_mode) {
|
1114 | status = loadJPEGFromBuffer(buf, len);
|
1115 | if (!status) status = assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG);
|
1116 | } else if (DATA_MIME == data_mode) {
|
1117 | status = decodeJPEGBufferIntoMimeSurface(buf, len);
|
1118 | }
|
1119 | #if defined(_MSC_VER)
|
1120 | else if (DATA_IMAGE == data_mode) {
|
1121 | status = loadJPEGFromBuffer(buf, len);
|
1122 | }
|
1123 | #endif
|
1124 | else {
|
1125 | status = CAIRO_STATUS_READ_ERROR;
|
1126 | }
|
1127 |
|
1128 | fclose(stream);
|
1129 | free(buf);
|
1130 | }
|
1131 |
|
1132 | return status;
|
1133 | }
|
1134 |
|
1135 | #endif
|
1136 |
|
1137 | #ifdef HAVE_RSVG
|
1138 |
|
1139 |
|
1140 |
|
1141 |
|
1142 |
|
1143 | cairo_status_t
|
1144 | Image::loadSVGFromBuffer(uint8_t *buf, unsigned len) {
|
1145 | _is_svg = true;
|
1146 |
|
1147 | cairo_status_t status;
|
1148 | GError *gerr = NULL;
|
1149 |
|
1150 | if (NULL == (_rsvg = rsvg_handle_new_from_data(buf, len, &gerr))) {
|
1151 | return CAIRO_STATUS_READ_ERROR;
|
1152 | }
|
1153 |
|
1154 | RsvgDimensionData *dims = new RsvgDimensionData();
|
1155 | rsvg_handle_get_dimensions(_rsvg, dims);
|
1156 |
|
1157 | width = naturalWidth = dims->width;
|
1158 | height = naturalHeight = dims->height;
|
1159 |
|
1160 | status = renderSVGToSurface();
|
1161 | if (status != CAIRO_STATUS_SUCCESS) {
|
1162 | g_object_unref(_rsvg);
|
1163 | return status;
|
1164 | }
|
1165 |
|
1166 | return CAIRO_STATUS_SUCCESS;
|
1167 | }
|
1168 |
|
1169 |
|
1170 |
|
1171 |
|
1172 | cairo_status_t
|
1173 | Image::renderSVGToSurface() {
|
1174 | cairo_status_t status;
|
1175 |
|
1176 | _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
|
1177 |
|
1178 | status = cairo_surface_status(_surface);
|
1179 | if (status != CAIRO_STATUS_SUCCESS) {
|
1180 | g_object_unref(_rsvg);
|
1181 | return status;
|
1182 | }
|
1183 |
|
1184 | cairo_t *cr = cairo_create(_surface);
|
1185 | cairo_scale(cr,
|
1186 | (double)width / (double)naturalWidth,
|
1187 | (double)height / (double)naturalHeight);
|
1188 | status = cairo_status(cr);
|
1189 | if (status != CAIRO_STATUS_SUCCESS) {
|
1190 | g_object_unref(_rsvg);
|
1191 | return status;
|
1192 | }
|
1193 |
|
1194 | gboolean render_ok = rsvg_handle_render_cairo(_rsvg, cr);
|
1195 | if (!render_ok) {
|
1196 | g_object_unref(_rsvg);
|
1197 | cairo_destroy(cr);
|
1198 | return CAIRO_STATUS_READ_ERROR;
|
1199 | }
|
1200 |
|
1201 | cairo_destroy(cr);
|
1202 |
|
1203 | _svg_last_width = width;
|
1204 | _svg_last_height = height;
|
1205 |
|
1206 | return status;
|
1207 | }
|
1208 |
|
1209 |
|
1210 |
|
1211 |
|
1212 |
|
1213 | cairo_status_t
|
1214 | Image::loadSVG(FILE *stream) {
|
1215 | _is_svg = true;
|
1216 |
|
1217 | struct stat s;
|
1218 | int fd = fileno(stream);
|
1219 |
|
1220 |
|
1221 | if (fstat(fd, &s) < 0) {
|
1222 | fclose(stream);
|
1223 | return CAIRO_STATUS_READ_ERROR;
|
1224 | }
|
1225 |
|
1226 | uint8_t *buf = (uint8_t *) malloc(s.st_size);
|
1227 |
|
1228 | if (!buf) {
|
1229 | fclose(stream);
|
1230 | return CAIRO_STATUS_NO_MEMORY;
|
1231 | }
|
1232 |
|
1233 | size_t read = fread(buf, s.st_size, 1, stream);
|
1234 | fclose(stream);
|
1235 |
|
1236 | cairo_status_t result = CAIRO_STATUS_READ_ERROR;
|
1237 | if (1 == read) result = loadSVGFromBuffer(buf, s.st_size);
|
1238 | free(buf);
|
1239 |
|
1240 | return result;
|
1241 | }
|
1242 |
|
1243 | #endif
|
1244 |
|
1245 |
|
1246 |
|
1247 |
|
1248 |
|
1249 | cairo_status_t Image::loadBMPFromBuffer(uint8_t *buf, unsigned len){
|
1250 | BMPParser::Parser parser;
|
1251 |
|
1252 |
|
1253 | uint8_t pixFmt[5] = {2, 1, 0, 3, 1};
|
1254 | parser.parse(buf, len, pixFmt);
|
1255 |
|
1256 | if (parser.getStatus() != BMPParser::Status::OK) {
|
1257 | errorInfo.reset();
|
1258 | errorInfo.message = parser.getErrMsg();
|
1259 | return CAIRO_STATUS_READ_ERROR;
|
1260 | }
|
1261 |
|
1262 | width = naturalWidth = parser.getWidth();
|
1263 | height = naturalHeight = parser.getHeight();
|
1264 | uint8_t *data = parser.getImgd();
|
1265 |
|
1266 | _surface = cairo_image_surface_create_for_data(
|
1267 | data,
|
1268 | CAIRO_FORMAT_ARGB32,
|
1269 | width,
|
1270 | height,
|
1271 | cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width)
|
1272 | );
|
1273 |
|
1274 |
|
1275 | cairo_status_t status = cairo_surface_status(_surface);
|
1276 | if (status) return status;
|
1277 |
|
1278 | _data = data;
|
1279 | parser.clearImgd();
|
1280 |
|
1281 | return CAIRO_STATUS_SUCCESS;
|
1282 | }
|
1283 |
|
1284 |
|
1285 |
|
1286 |
|
1287 |
|
1288 | cairo_status_t Image::loadBMP(FILE *stream){
|
1289 | struct stat s;
|
1290 | int fd = fileno(stream);
|
1291 |
|
1292 |
|
1293 | if (fstat(fd, &s) < 0) {
|
1294 | fclose(stream);
|
1295 | return CAIRO_STATUS_READ_ERROR;
|
1296 | }
|
1297 |
|
1298 | uint8_t *buf = new uint8_t[s.st_size];
|
1299 |
|
1300 | if (!buf) {
|
1301 | fclose(stream);
|
1302 | errorInfo.set(NULL, "malloc", errno);
|
1303 | return CAIRO_STATUS_NO_MEMORY;
|
1304 | }
|
1305 |
|
1306 | size_t read = fread(buf, s.st_size, 1, stream);
|
1307 | fclose(stream);
|
1308 |
|
1309 | cairo_status_t result = CAIRO_STATUS_READ_ERROR;
|
1310 | if (read == 1) result = loadBMPFromBuffer(buf, s.st_size);
|
1311 | delete[] buf;
|
1312 |
|
1313 | return result;
|
1314 | }
|
1315 |
|
1316 |
|
1317 |
|
1318 |
|
1319 |
|
1320 | Image::type
|
1321 | Image::extension(const char *filename) {
|
1322 | size_t len = strlen(filename);
|
1323 | filename += len;
|
1324 | if (len >= 5 && 0 == strcmp(".jpeg", filename - 5)) return Image::JPEG;
|
1325 | if (len >= 4 && 0 == strcmp(".gif", filename - 4)) return Image::GIF;
|
1326 | if (len >= 4 && 0 == strcmp(".jpg", filename - 4)) return Image::JPEG;
|
1327 | if (len >= 4 && 0 == strcmp(".png", filename - 4)) return Image::PNG;
|
1328 | if (len >= 4 && 0 == strcmp(".svg", filename - 4)) return Image::SVG;
|
1329 | return Image::UNKNOWN;
|
1330 | }
|
1331 |
|
1332 |
|
1333 |
|
1334 |
|
1335 |
|
1336 | int
|
1337 | Image::isJPEG(uint8_t *data) {
|
1338 | return 0xff == data[0] && 0xd8 == data[1];
|
1339 | }
|
1340 |
|
1341 |
|
1342 |
|
1343 |
|
1344 |
|
1345 | int
|
1346 | Image::isGIF(uint8_t *data) {
|
1347 | return 'G' == data[0] && 'I' == data[1] && 'F' == data[2];
|
1348 | }
|
1349 |
|
1350 |
|
1351 |
|
1352 |
|
1353 |
|
1354 | int
|
1355 | Image::isPNG(uint8_t *data) {
|
1356 | return 'P' == data[1] && 'N' == data[2] && 'G' == data[3];
|
1357 | }
|
1358 |
|
1359 |
|
1360 |
|
1361 |
|
1362 | int
|
1363 | Image::isSVG(uint8_t *data, unsigned len) {
|
1364 | for (unsigned i = 3; i < len; i++) {
|
1365 | if ('<' == data[i-3]) {
|
1366 | switch (data[i-2]) {
|
1367 | case '?':
|
1368 | case '!':
|
1369 | break;
|
1370 | case 's':
|
1371 | return ('v' == data[i-1] && 'g' == data[i]);
|
1372 | default:
|
1373 | return false;
|
1374 | }
|
1375 | }
|
1376 | }
|
1377 | return false;
|
1378 | }
|
1379 |
|
1380 |
|
1381 |
|
1382 |
|
1383 |
|
1384 | int Image::isBMP(uint8_t *data, unsigned len) {
|
1385 | if(len < 2) return false;
|
1386 | std::string sig = std::string(1, (char)data[0]) + (char)data[1];
|
1387 | return sig == "BM" ||
|
1388 | sig == "BA" ||
|
1389 | sig == "CI" ||
|
1390 | sig == "CP" ||
|
1391 | sig == "IC" ||
|
1392 | sig == "PT";
|
1393 | }
|