2 /*] Copyright (c) 2009-2011, Charles McGarvey [*****************************
3 **] All rights reserved.
5 * Distributable under the terms and conditions of the 2-clause BSD license;
6 * see the file COPYING for a complete text of the license.
8 *****************************************************************************/
16 #include <stlplus/portability/file_system.hpp>
30 MOOF_REGISTER_RESOURCE(image
, bmp
, textures
);
31 MOOF_REGISTER_RESOURCE(image
, png
, textures
);
34 static int higher_power_of_two(int input
)
37 while (value
<= input
) value
<<= 1;
41 static void read_from_stream(png_structp context
,
42 png_bytep data
, png_size_t length
)
44 std::istream
& stream(*(std::istream
*)png_get_io_ptr(context
));
45 stream
.read((char*)data
, length
);
49 struct texture_attributes
51 texture_attributes() :
52 min_filter(GL_NEAREST
),
53 mag_filter(GL_NEAREST
),
56 wrap_s(GL_CLAMP_TO_EDGE
),
57 wrap_t(GL_CLAMP_TO_EDGE
) {}
59 void init(const std::string
& info
)
64 script::slot g
= script
.globals();
65 #define EXPORT_CONSTANT(K) g.set_field(#K, GL_##K)
66 EXPORT_CONSTANT(CLAMP
);
67 EXPORT_CONSTANT(CLAMP_TO_EDGE
);
68 EXPORT_CONSTANT(REPEAT
);
69 EXPORT_CONSTANT(LINEAR
);
70 EXPORT_CONSTANT(NEAREST
);
71 EXPORT_CONSTANT(LINEAR_MIPMAP_LINEAR
);
72 EXPORT_CONSTANT(LINEAR_MIPMAP_NEAREST
);
73 EXPORT_CONSTANT(NEAREST_MIPMAP_LINEAR
);
74 EXPORT_CONSTANT(NEAREST_MIPMAP_NEAREST
);
75 #undef export_constant
77 if (script
.do_string(info
) != script::success
)
85 log_info("loading texture information...");
87 script::slot globals
= script
.globals();
88 globals
.get(min_filter
, "min_filter");
89 globals
.get(mag_filter
, "mag_filter");
90 globals
.get(tile_s
, "tile_s");
91 globals
.get(tile_t
, "tile_t");
92 globals
.get(wrap_s
, "wrap_s");
93 globals
.get(wrap_t
, "wrap_t");
106 static SDL_Surface
* load_png(const std::string
& path
, texture_attributes
& attribs
)
108 std::ifstream
file(path
.c_str(), std::ifstream::binary
);
110 throw std::runtime_error("no valid image found at " + path
);
112 png_byte signature
[8];
123 bytesRead
= file
.read((char*)signature
, sizeof(signature
)).gcount();
124 if (bytesRead
< sizeof(signature
) ||
125 png_sig_cmp(signature
, 0, sizeof(signature
)) != 0) throw 0;
132 context(png_create_read_struct(PNG_LIBPNG_VER_STRING
,
134 info(png_create_info_struct(context
))
136 if (!context
|| !info
) throw 0;
140 png_destroy_read_struct(context
? &context
: 0,
141 info
? &info
: 0, 0);
145 if (setjmp(png_jmpbuf(png
.context
))) throw 0;
147 png_set_read_fn(png
.context
, (void*)&file
, read_from_stream
);
148 png_set_sig_bytes(png
.context
, sizeof(signature
));
149 png_read_info(png
.context
, png
.info
);
151 bpp
= png_get_bit_depth(png
.context
, png
.info
);
152 colors
= png_get_color_type(png
.context
, png
.info
);
155 case PNG_COLOR_TYPE_PALETTE
:
156 png_set_palette_to_rgb(png
.context
);
159 case PNG_COLOR_TYPE_GRAY
:
160 if (bpp
< 8) png_set_expand(png
.context
);
163 case PNG_COLOR_TYPE_GRAY_ALPHA
:
164 png_set_gray_to_rgb(png
.context
);
168 if (bpp
== 16) png_set_strip_16(png
.context
);
170 png_read_update_info(png
.context
, png
.info
);
172 bpp
= png_get_bit_depth(png
.context
, png
.info
);
173 int channels
= png_get_channels(png
.context
, png
.info
);
174 int depth
= bpp
* channels
;
177 bool texture
= false;
178 png_get_text(png
.context
, png
.info
, &texts
, &nutext_s
);
179 for (int i
= 0; i
< nutext_s
; ++i
)
181 if (std::string(texts
[i
].key
) == "X-Yoink-Texture")
183 attribs
.init(texts
[i
].text
);
189 int width
= png_get_image_width(png
.context
, png
.info
);
190 int height
= png_get_image_height(png
.context
, png
.info
);
191 int pitch
= png_get_rowbytes(png
.context
, png
.info
);
192 char* pixels
= new char[height
* pitch
];
194 rows
= new png_bytep
[height
];
197 log_debug("texture detected; loading flipped");
198 for (int i
= 0; i
< height
; ++i
)
200 rows
[height
-1-i
] = (png_bytep
)(pixels
+
201 i
* channels
* width
);
206 log_debug("no texture attributes found");
207 for (int i
= 0; i
< height
; ++i
)
209 rows
[i
] = (png_bytep
)(pixels
+
210 i
* channels
* width
);
214 png_read_image(png
.context
, rows
);
217 png_read_end(png
.context
, 0);
219 SDL_Surface
* src
= SDL_CreateRGBSurfaceFrom
226 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
238 if (!src
) throw std::runtime_error(SDL_GetError());
242 static SDL_Surface
* load_bmp(const std::string
& path
)
244 return SDL_LoadBMP(path
.c_str());
249 static void save_bmp(SDL_Surface
* image
, const std::string
& path
)
251 if (SDL_SaveBMP(image
, path
.c_str()) != 0)
252 throw std::runtime_error(SDL_GetError());
256 static void destroy_context(SDL_Surface
* context
)
258 if (context
->flags
& SDL_PREALLOC
) delete[] (char*)context
->pixels
;
259 SDL_FreeSurface(context
);
264 image::image(const std::string
& path
) :
267 min_filter_(GL_NEAREST
),
268 mag_filter_(GL_NEAREST
),
271 wrap_s_(GL_CLAMP_TO_EDGE
),
272 wrap_t_(GL_CLAMP_TO_EDGE
)
274 std::string ext
= stlplus::extension_part(path
);
276 SDL_Surface
* context
= 0;
279 texture_attributes attribs
;
280 context
= load_png(path
, attribs
);
281 pixels_
= (char*)context
->pixels
;
283 height_
= context
->h
;
285 pitch_
= context
->pitch
;
287 min_filter_
= attribs
.min_filter
;
288 mag_filter_
= attribs
.mag_filter
;
289 tile_s_
= attribs
.tile_s
;
290 tile_t_
= attribs
.tile_t
;
291 wrap_s_
= attribs
.wrap_s
;
292 wrap_t_
= attribs
.wrap_t
;
295 else if (ext
== "bmp")
297 context
= load_bmp(path
);
298 pixels_
= (char*)context
->pixels
;
300 height_
= context
->h
;
302 pitch_
= context
->pitch
;
305 // TODO leaking context
315 void image::postprocess()
317 if (1 == tile_s_
&& 1 == tile_t_
) return;
319 SDL_Surface
* src
= SDL_CreateRGBSurfaceFrom
326 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
338 SDL_Surface
* dst
= SDL_CreateRGBSurface(
340 higher_power_of_two(width_
),
341 higher_power_of_two(height_
),
343 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
357 SDL_SetAlpha(src
, 0, 128);
358 SDL_SetAlpha(dst
, 0, 128);
360 int src_tilew
= src
->w
/ tile_s_
;
361 int src_tileh
= src
->h
/ tile_t_
;
362 int dst_tilew
= dst
->w
/ tile_s_
;
363 int dst_tileh
= dst
->h
/ tile_t_
;
365 for (int t
= 0; t
< tile_t_
; ++t
) for (int s
= 0; s
< tile_s_
; ++s
)
369 srcrect
.x
= s
* src_tilew
;
370 srcrect
.y
= t
* src_tileh
;
371 srcrect
.w
= src_tilew
;
372 srcrect
.h
= src_tileh
;
373 dstrect
.x
= s
* dst_tilew
+ (src_tilew
/ 2);
374 dstrect
.y
= t
* dst_tileh
+ (src_tileh
/ 2);
375 log_warning("SRC", srcrect
.x
, srcrect
.y
, srcrect
.w
, srcrect
.h
);
376 log_warning("DST", dstrect
.x
, dstrect
.y
);
377 SDL_BlitSurface(src
, &srcrect
, dst
, &dstrect
);
379 srcrect
.x
= s
* src_tilew
;
380 srcrect
.y
= t
* src_tileh
;
382 srcrect
.h
= src_tileh
;
383 dstrect
.y
= t
* dst_tileh
+ (src_tileh
/ 2);
384 for (int x
= s
* dst_tilew
+ (src_tilew
/ 2) - 1; s
* dst_tilew
<= x
; --x
)
387 SDL_BlitSurface(src
, &srcrect
, dst
, &dstrect
);
390 srcrect
.x
= (s
+ 1) * src_tilew
- 1;
391 srcrect
.y
= t
* src_tileh
;
393 srcrect
.h
= src_tileh
;
394 dstrect
.y
= t
* dst_tileh
+ (src_tileh
/ 2);
395 for (int x
= (s
+ 1) * dst_tilew
- (src_tilew
/ 2); x
< (s
+ 1) * dst_tilew
; ++x
)
398 SDL_BlitSurface(src
, &srcrect
, dst
, &dstrect
);
401 srcrect
.x
= s
* src_tilew
;
402 srcrect
.y
= t
* src_tileh
;
403 srcrect
.w
= src_tilew
;
405 dstrect
.x
= s
* dst_tilew
+ (src_tilew
/ 2);
406 for (int y
= t
* dst_tileh
+ (src_tileh
/ 2) - 1; t
* dst_tileh
<= y
; --y
)
409 SDL_BlitSurface(src
, &srcrect
, dst
, &dstrect
);
412 srcrect
.x
= s
* src_tilew
;
413 srcrect
.y
= (t
+ 1) * src_tileh
- 1;
414 srcrect
.w
= src_tilew
;
416 dstrect
.x
= s
* dst_tilew
+ (src_tilew
/ 2);
417 for (int y
= (t
+ 1) * dst_tileh
- (src_tileh
/ 2); y
< (t
+ 1) * dst_tileh
; ++y
)
420 SDL_BlitSurface(src
, &srcrect
, dst
, &dstrect
);
423 SDL_FreeSurface(src
);
425 char* pixels
= new char[dst
->w
* dst
->pitch
];
426 SDL_Surface
* finaldst
= SDL_CreateRGBSurfaceFrom
433 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
445 SDL_BlitSurface(dst
, 0, finaldst
, 0);
447 //SDL_SaveBMP(dst, (stlplus::basename_part(path) + ".bmp").c_str());
448 SDL_FreeSurface(dst
);
450 width_
= finaldst
->w
;
451 height_
= finaldst
->h
;
452 pitch_
= finaldst
->pitch
;
454 SDL_FreeSurface(finaldst
);
461 void image::fix_uv(std::vector
<vector2
>& p
) const
463 vector2
mid(SCALAR(0.0), SCALAR(0.0));
464 for (int i
= 0; i
< p
.size(); ++i
)
473 mid
[0] = std::floor(mid
[0]);
474 mid
[1] = std::floor(mid
[1]);
475 log_warning("midpoint:", mid
);
479 scalar src_width
= width_
>> 1;
480 scalar src_height
= height_
>> 1;
481 int src_tilew
= src_width
/ tile_s_
;
482 int src_tileh
= src_height
/ tile_t_
;
483 int dst_tilew
= width_
/ tile_s_
;
484 int dst_tileh
= height_
/ tile_t_
;
486 for (int i
= 0; i
< p
.size(); ++i
)
488 //scalar s = p[i][0] * src_width;
489 scalar x
= s
* dst_tilew
+ (src_tilew
/ 2) + (p
[i
][0] * src_width
- s
* src_tilew
);
490 p
[i
][0] = x
/ width_
;
491 //scalar t = p[i][1] * src_height;
492 scalar y
= t
* dst_tileh
+ (src_tileh
/ 2) + (p
[i
][1] * src_height
- t
* src_tileh
);;
493 p
[i
][1] = y
/ height_
;
498 void image::set_as_icon() const
502 SDL_Surface
* context
= SDL_CreateRGBSurfaceFrom
509 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
522 SDL_WM_SetIcon(context
, 0);
523 SDL_FreeSurface(context
);
526 bool image::tile_coordinates(int index
, scalar coords
[8]) const
528 // make sure the index represents a real tile
529 if (index
< 0 && index
>= tile_s_
* tile_t_
) return false;
531 scalar w
= 1.0 / scalar(tile_s_
);
532 scalar h
= 1.0 / scalar(tile_t_
);
534 coords
[0] = scalar(index
% tile_s_
) * w
;
535 coords
[1] = (scalar(tile_t_
- 1) - scalar(index
/ tile_s_
)) * h
;
536 coords
[2] = coords
[0] + w
;
537 coords
[3] = coords
[1];
538 coords
[4] = coords
[2];
539 coords
[5] = coords
[1] + h
;
540 coords
[6] = coords
[0];
541 coords
[7] = coords
[5];
546 void image::bind() const
548 ASSERT(video::ready() && "should have a video context set");
554 if (object_
!= global_object_
)
556 glBindTexture(GL_TEXTURE_2D
, (GLuint
)object_
);
557 global_object_
= object_
;
561 void image::reset_binding()
563 ASSERT(video::ready() && "should have a video context set");
565 glBindTexture(GL_TEXTURE_2D
, 0);
570 * Upload the image to GL so that it will be accessible by a much more
571 * manageable handle and hopefully reside in video memory.
573 void image::upload_to_gl() const
575 if (object_
) return; // already loaded
577 glGenTextures(1, (GLuint
*)&object_
);
578 glBindTexture(GL_TEXTURE_2D
, (GLuint
)object_
);
581 if (channels_
== 3) mode
= GL_RGB
;
601 // we want to know when the GL context is recreated
602 //dispatcher& dispatcher = dispatcher::global();
603 //new_context_ = dispatcher.add_target("video.newcontext",
604 //boost::bind(&image::context_recreated, this));
605 // FIXME this has const issues
608 void image::unload_from_gl() const
612 if (object_
== global_object_
) global_object_
= 0;
613 glDeleteTextures(1, (GLuint
*)&object_
);
618 void image::context_recreated()
620 object_
= global_object_
= 0;
625 * Sets some texture properties such as the filters and external
626 * coordinate behavior.
628 void image::set_properties() const
630 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, min_filter_
);
631 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, mag_filter_
);
632 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, wrap_s_
);
633 glTexParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, wrap_t_
);
637 unsigned image::global_object_
= 0;