diff --git a/ChangeLog-ivan b/ChangeLog-ivan new file mode 100644 index 000000000..f2d537244 --- /dev/null +++ b/ChangeLog-ivan @@ -0,0 +1,1266 @@ +2008-04-25 Øyvind Kolås + + * clutter/eglnative/clutter-backend-egl.c: (clutter_egl_display): + removed platform specific build hack rpl_malloc and rpl_free wrappers. + + * clutter/eglnative/clutter-backend-egl.h: + * clutter/eglnative/clutter-egl.h: + * clutter/eglnative/clutter-stage-egl.h: removed platform specific + extra includes from eglnative. + +2008-04-25 Øyvind Kolås + + * tests/test-pixmap.c: (main): only compile this test if + HAVE_CLUTTER_GLX is defined. + +2008-04-25 Øyvind Kolås + + Merged back changes from the fruity flavour into the original native + flavour. Changes for dealing with multiple stages. + + * clutter/eglnative/clutter-backend-egl.c: + * clutter/eglnative/clutter-backend-egl.h: + * clutter/eglnative/clutter-egl.h: + * clutter/eglnative/clutter-stage-egl.c: + * clutter/eglnative/clutter-stage-egl.h: + +2008-04-25 Neil Roberts + + * clutter/cogl/cogl.h.in: Tiny fix to gtk-doc for cogl_fog.set + +2008-04-25 Neil Roberts + + * clutter/cogl/gles/cogl-texture.c (cogl_texture_polygon): + * clutter/cogl/gl/cogl-texture.c (cogl_texture_polygon): Added a + warning if the call will fail. + + * clutter/cogl/cogl.h.in: Added a note in the gtk-doc about not + supporting repeated textures in cogl_texture_polygon. + +2008-04-24 Øyvind Kolås + + * configure.ac: added fruity backend. + * clutter/clutter-main.c: (clutter_main): special case invoking main() + for the fruity backend. + * clutter/clutter-stage.c: (clutter_stage_set_title): do not call the + set_title vfunc if no implementation exist in the interface. + + * clutter/fruity/Makefile.am: + * clutter/fruity/clutter-backend-fruity.c: + * clutter/fruity/clutter-backend-fruity.h: + * clutter/fruity/clutter-fruity.c: + * clutter/fruity/clutter-fruity.h: + * clutter/fruity/clutter-stage-fruity.c: + * clutter/fruity/clutter-stage-fruity.h: added fruity backend. + +2008-04-24 Matthew Allum + + * clutter/clutter-texture.c: + Add a 'disable-slicing' property. + +2008-04-24 Ivan Leben + + * clutter/clutter-feature.c: + (_clutter_features_from_cogl:) oops, forgot to return. + +2008-04-24 Ivan Leben + + * clutter/clutter-feature.c: + Map cogl to clutter features before or-ing with clutter flags + obtained from backend. + + * clutter/cogl/cogl.h.in: + Removed SYNC_TO_VBLANK from cogl feature flags, since its only + checked on clutter level. Renumbered flags in ascending order. + + * clutter/cogl/gles/cogl.c: + (_cogl_features_init:) Adds COGL_FEATURE_TEXTURE_READ_PIXELS + flag to features. + +2008-04-24 Neil Roberts + + * clutter/cogl/cogl.h.in: Documented remaining functions up to the + texture API except cogl_check_extension, cogl_perspective and + cogl_alpha_func. + +2008-04-24 Neil Roberts + + * clutter/clutter-texture.c (struct _ClutterTexturePrivate): Make + max_tile_waste signed. + (clutter_texture_class_init): Fix documentation for tile-waste + property. + (clutter_texture_get_cogl_texture), + (clutter_texture_set_cogl_texture): Added 'since' to the gtk-doc. + (clutter_texture_set_max_tile_waste): Make the max_tile_waste + parameter signed and fix the documentation. + (clutter_texture_get_max_tile_waste): Make the return value signed + and fix the documentation. + + * clutter/clutter-texture.h: Fix type for + {get,set}_max_tile_waste. + + * clutter/clutter-sections.txt: Added + clutter_texture_{get,set}_max_tile_waste and + clutter_texture_set_cogl_texture. + +2008-04-24 Ivan Leben + + * clutter/cogl/cogl.h.in: + * clutter/cogl/gl/(es)/cogl-texture.c: + cogl_texture_get_max_waste returns gint instead of guint. + +2008-04-24 Ivan Leben + + * clutter/cogl/cogl.h.in: + Texture *_new_* functions max_waste argument changed to signed + integer. + + * clutter/cogl/gl(es)/cogl-texture.c: + (_cogl_texture_slices_create:) Returns FALSE if max_waste is -1 + and size of initial slice is not supported. + + (_cogl_texture_slices_free:) Fixed so that slice_gl_handles + array is freed when texture created from foreign. + + (_cogl_texture_free:) New function - calls other *_free + functions to simplify destruction. + + (*_new_* functions): max_waste signed, fixed handling of + failed texture construction so that the resources allocated + so far are actually freed before bailing out. + +2008-04-24 Neil Roberts + + * clutter/cogl/cogl.h.in (cogl_scale): Fixed name of the y + parameter. + +2008-04-24 Robert Bragg + + * clutter/eglx/clutter-stage-egl.c + * clutter/cogl/gl/cogl-texture.c + * clutter/cogl/gles/cogl-texture.c + * tests/test-pixmap.c: + Minor tidy up of some allocations: s/malloc/g_malloc and s/free/g_free + (except I left cogl/common/stb_image.c untouched) + These had been causing some cross compile issues for me. + +2008-04-24 Ivan Leben + + * clutter/cogl/gl(es)/cogl-texture.c: + (cogl_texture_is_sliced, cogl_texture_get_gl_texture:) + Fixed a copy-paste typo. Thanks to Matthew Allum for spotting. + +2008-04-24 Ivan Leben + + * clutter/cogl/cogl.h.in: Changed flags in CoglTargetBuffer + enum to occupy separate bits so COGL_WINDOW_BUFFER and + COGL_MASK_BUFFER can be or-ed together. + + * clutter/cogl/gl(es)/cogl-fbo.c: (cogl_draw_buffer:) handles + the combination of window and mask buffer targets + + * clutter/cogl/gles/cogl-textures.c: + Implemented cogl_texture_getset in GLES. Limitations are two: + a) viewport must be inside the window and have at least width and + height of 1 pixel + b) framebuffer alpha plane required to properly obtain data for + alpha texture channel + +2008-04-23 Robert Bragg + + * clutter/sdl/Makefile.am + * clutter/glx/Makefile.am + * clutter/pango/Makefile.am + * clutter/cogl/gl/Makefile.am + * clutter/cogl/common/Makefile.am + * clutter/cogl/gles/Makefile.am + * clutter/win32/Makefile.am + * clutter/x11/Makefile.am + * clutter/osx/Makefile.am + * clutter/Makefile.am + * tests/Makefile.am: + Adds support for out of tree builds. Tested building for arm+eglx + glx and mingw+win32. + +>>>>>>> .r2586 +2008-04-23 Neil Roberts + + * tests/Makefile.am: Don't build test-pixmap if we are not + targetting one of the X11-based backends. + + * configure.ac: Define an AM_CONDITIONAL when we are building + for either of the X11-based backends. + +2008-04-23 Emmanuele Bassi + + * clutter/clutter-texture.c: + (clutter_texture_set_property), + (clutter_texture_class_init): Add a :filename property, writable + only, for setting the filename of the texture data. + + (clutter_texture_set_custom_property), + (clutter_scriptable_iface_init): Override the scriptable interface + to allow using relative paths for the :filename property; chain + up to the parent class implementation. + + * tests/test-script.json: Update to use "filename" instead of + "pixbuf". + +2008-04-23 Emmanuele Bassi + + * clutter/clutter-actor.[ch]: Sync up with trunk, to reduce the + chunks between the clutter-ivan branch and trunk when merging. + +2008-04-23 Neil Roberts + + * clutter/clutter-actor.c (clutter_actor_set_property): When one + of the rotation properties is set it also tries to set the + rotation center to what is was already. clutter_actor_set_rotation + expects integer co-ordinates but we were passing them directly + from the actor private data which stores them as ClutterUnits + +2008-04-22 Robert Bragg + + * clutter/x11/clutter-x11-texture-pixmap.c: + clutter_x11_texture_pixmap_update_area_real simply returns if no + pixmap has been set yet so we dont hit an X error after trying + XGet[Sub]Image. + +2008-04-22 Robert Bragg + + * clutter/cogl/common/cogl-bitmap-pixbuf.c: + Tweaks the calculation of last_row_size in _cogl_bitmap_from_file + (USE_GDKPIXBUF) to fix an overflow page fault. + +2008-04-22 Neil Roberts + + * clutter/cogl/cogl.h.in: + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gles/cogl-texture.c: + * doc/reference/cogl/cogl-sections.txt: Removed + cogl_texture_can_polygon. + +2008-04-22 Neil Roberts + + * doc/reference/cogl/Makefile.am (INCLUDES): + * doc/reference/clutter/Makefile.am (INCLUDES): + Added the clutter directory to the include path so that + cogl/cogl.h can be included when building cogl-scan and + clutter-scan. + +2008-04-22 Neil Roberts + + Relocated the installation location of the cogl header to + clutter-VERSION/cogl/cogl.h instead of + clutter-VERSION/clutter/cogl.h. + + * configure.ac: Now generates cogl/cogl-defines-*.gl from the + cogl-defines.h in the appropriate directory. cogl.h is also + generated to include the right cogl-defines-*.h + + * clutter/cogl/gl/Makefile.am: + * clutter/cogl/gles/Makefile.am: Install cogl headers to + clutter-VERSION/cogl instead of clutter-VERSION/clutter. + + * clutter/cogl/cogl.h: Rename to cogl.h.in and have the configure + script filter it so that it can include the right + cogl-defines-*.h. + + * clutter/cogl/gles/cogl-defines.h: Rename to cogl-defines.h.in so + that the configure script can copy it to the parent directory. + + * clutter/cogl/gles/cogl-fbo.c: + * clutter/cogl/gl/cogl-fbo.c: Remove inclusion of cogl-defines.h + because it has changed its name and it is included indirectly by + cogl.h anyway. + + * clutter/cogl/common/Makefile.am (INCLUDES): Added the clutter + folder to the include path. + + * clutter/clutter-texture.h: Properly include the COGL header as + and use CoglHandle instead of void* + + * clutter/clutter-clone-texture.c: + * clutter/clutter-feature.c: + * clutter/clutter-group.c: + * clutter/clutter-main.c: + * clutter/clutter-rectangle.c: + * clutter/clutter-shader.c: + * clutter/clutter-stage.c: + * clutter/clutter-texture.c: + * clutter/clutter-actor.c: + * clutter/glx/clutter-stage-glx.c: + * clutter/glx/clutter-backend-glx.c: + * clutter/glx/clutter-glx-texture-pixmap.c: + * clutter/pango/pangoclutter-render.c: + * clutter/sdl/clutter-stage-sdl.c: + * clutter/win32/clutter-backend-win32.c: + * clutter/win32/clutter-stage-win32.c: + * clutter/x11/clutter-backend-x11.c: + * clutter/x11/clutter-stage-x11.c: + * clutter/x11/clutter-x11-texture-pixmap.c: + Include the COGL header as cogl/cogl.h instead of cogl.h + + * clutter/pango/Makefile.am: + * clutter/osx/Makefile.am: + * clutter/glx/Makefile.am: + * clutter/sdl/Makefile.am: + * clutter/win32/Makefile.am: + * clutter/x11/Makefile.am: + * clutter/Makefile.am: Remove the two COGL include + directories and add the clutter folder so that the cogl header can + be included as cogl/cogl.h + + * tests/Makefile.am: Added the clutter folder to the include path + and removed the special build flags for the COGL tests. + + * tests/test-cogl-offscreen: + * tests/test-cogl-tex-convert: + * tests/test-cogl-tex-foreign: + * tests/test-cogl-tex-getset: + * tests/test-cogl-tex-polygon: + * tests/test-cogl-tex-tile: + Include COGL header as instead of "cogl.h" + +2008-04-22 Øyvind Kolås + + * clutter/cogl/common/stb_image.c: only include emmintrin.h when + STBI_SIMD is explictly defined before including stb_image.[ch] to + make stb_image compile for ARM. + +2008-04-22 Øyvind Kolås + + * clutter/eglx/clutter-stage-egl.c: removed ::draw_to_pixbuf. + +2008-04-22 Ivan Leben + + * clutter/cogl/gl(es)/cogl-texture.c: + (cogl_texture_new_from_foreign:) Sets waste value for the single + slice and tex->wrap_mode. This fixes test-cogl-tex-foreign after + hardware tiling has been corrected by factoring out the waste from + texture coordinates. + +2008-04-22 Ivan Leben + + * clutter/cogl/gl/cogl-texture.c (_cogl_texture_slices_create:) + Moved check for harware tiling availability so that n_slices is + actually initialized before being used in the check. Fixes + test-cogl-tex-tile after introduction of cogl_texture_polygon. + +2008-04-22 Øyvind Kolås + + * clutter/clutter-stage-window.h: removed draw_to_pixbuf call from + class. + * clutter/glx/clutter-stage-glx.c: removed + clutter_stage_glx_draw_to_pixbuf. + * clutter/clutter-stage.[ch]: (clutter_stage_read_pixels): made + clutter_stage_read_pixels replace clutter_stage_snapshot. + * tests/test-stage-read-pixels.c: added test that extends test-actors + with a on stage snapshot of the stage behind the circle of hands. + * tests/Makefile.am: added test. + +2008-04-21 Neil Roberts + + * clutter/cogl/gles/cogl-texture.c (_cogl_texture_quad_hw): + * clutter/cogl/gl/cogl-texture.c (_cogl_texture_quad_hw): + Fixed so that if the texture is sliced then it doesn't include the + waste part of the texture in the range of the texture coordinates. + +2008-04-21 Neil Roberts + + * clutter/cogl/cogl.h: Added cogl_texture_can_polygon and + cogl_texture_polygon. + (struct _CoglTextureVertex): New public structure to represent a + vertex with texture coordinates and a color to pass to + cogl_texture_polygon. + + * clutter/cogl/gl/cogl-texture.c (_cogl_texture_slices_create): + The wrap mode for the texture is now stored in the texture + structure so that it can be quickly restored after + cogl_texture_polygon temporarily changes it. It now also sets a + transparent border color for each texture to make + cogl_texture_polygon easier to work with sliced textures. + (cogl_texture_can_polygon, cogl_texture_polygon): New public + functions. + + * clutter/cogl/gl/cogl-texture.h (struct _CoglTexture): Added + wrap_mode + + * clutter/cogl/gles/cogl-context.h (struct CoglContext): Added a + global resizable array to store vertices in to pass to + glDrawArrays from cogl_texture_polygon. + + * clutter/cogl/gles/cogl-context.c (cogl_create_context): Added + initialisers for texture_vertices and textures_vertices_size. + (cogl_destroy_context): Frees texture_vertices. + + * clutter/cogl/gles/cogl-internal.h (COGL_ENABLE_COLOR_ARRAY): + Added a bit to enable the color array. + + * clutter/cogl/gles/cogl-texture.c (cogl_texture_can_polygon) + (cogl_texture_polygon): New public functions. + + * clutter/cogl/gles/cogl.c (cogl_enable): Added test for enabling + the color array. + + * tests/Makefile.am: Added test-cogl-tex-polygon. + + * tests/test-cogl-tex-polygon.c: New test for cogl_texture_polygon + +2008-04-18 Øyvind Kolås + + * configure.ac: added a configure time option to build without + depending on GdkPixbuf that uses stb_image.c instead, this build + type is experimental and can be enabled by passing + --with-imagebackend=internal to ./configure. + * clutter/cogl/common/stb_image.c: new file. + * clutter/cogl/common/Makefile.am: added new file. + * clutter/cogl/common/cogl-bitmap-pixbuf.c: + (_cogl_bitmap_from_file): added alternate codepath. + + * clutter/clutter-script.c: + * clutter/clutter-script-parser.c: #include and disable + code referring to GdkPixbuf is USE_GDKPIXBUF is not defined. + + * clutter/clutter-stage.[ch]: + * clutter/clutter-stage.h: + * clutter/clutter-stage-window.h: + * clutter/glx/clutter-stage-glx.c: + * clutter/x11/clutter-stage-x11.c: + * tests/test-pixmap.c: + * tests/test-textures.c: disable code referring to GdkPixbuf if + USE_GDKPIXBUF is not defined. + +2008-04-17 Neil Roberts + + Applied patch from bug #874 + + * clutter/cogl/gles/cogl.c (cogl_clip_set): + * clutter/cogl/gl/cogl.c (cogl_clip_set): If four clip planes are + available then attempt to use those to implement the clip rect, + otherwise resort to the stencil buffer. + + * clutter/cogl/cogl.h (enum CoglFeatureFlags): Added feature flags + for the stencil buffer and having four clip planes. + +2008-04-17 Øyvind Kolås + + * clutter/clutter-deprecated.h: added deprecation warning for + clutter_texture_new_from_pixmap. + +2008-04-16 Neil Roberts + + * tests/test-multistage.c (on_button_press): Fix to use the new + clutter_texture_new_from_file function instead of + clutter_texture_new_from_pixbuf. + +2008-04-16 Neil Roberts + + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gles/cogl-texture.c: + Fixed tests for COGL_DEBUG in texture referencing debugging + statements. (It's defined as 0 when disabled instead of not being + defined at all). oops + +2008-04-15 Ivan Leben + + * tests: update svn:ignore + +2008-04-15 Ivan Leben + + * clutter/cogl/cogl.h: + * clutter/cogl/common/cogl-bitmap.c: + * clutter/cogl/common/cogl-bitmap-fallback.c: + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gles/cogl-texture.c: + New pixel format COGL_PIXEL_FORMAT_G_8 and appropriate + conversions to / from RGBA implemented. + + * tests/test-cogl-tex-convert.c: + Bottom-right image uses G_8 format instead of RGB_888. + + * clutter/cogl/cogl.h: + * clutter/cogl/gl/cogl-texture.h: + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gles/cogl-texture.h: + * clutter/cogl/gles/cogl-texture.c: + New function cogl_texture_new_from_foreign constructs a + cogl texture from an existing GL texture object. + + * tests/test-cogl-tex-foreign.c: + New test app for cogl_texture_new_from_foreign. + +2008-04-15 Neil Roberts + + * clutter/clutter-texture.c (clutter_texture_set_property) + (clutter_texture_get_property, clutter_texture_class_init): Added + the 'cogl-texture' property. + (clutter_texture_handle_get_type): Added a function to get a boxed + type wrapper around a CoglHandle for a texture. + (clutter_texture_set_cogl_texture): Emits a notification signal + when the cogl-texture property is changed. + + * clutter/clutter-texture.h: Added a boxed type wrapper for a COGL + texture handle. + + * clutter/cogl/gl/cogl-texture.c (cogl_texture_new_with_size) + (cogl_texture_new_from_data, cogl_texture_new_from_file) + (cogl_texture_ref, cogl_texture_unref): Added debugging statements + for the texture referencing counting. + +2008-04-15 Neil Roberts + + Added reference counting to CoglHandles + + * tests/test-cogl-offscreen.c (test_coglbox_dispose): + * clutter/clutter-texture.c: Replaced calls to + cogl_offscreen_destroy with cogl_offscreen_unref. + + * clutter/cogl/gl/cogl-texture.h: + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gles/cogl-texture.h: + * clutter/cogl/gles/cogl-texture.c: + * clutter/cogl/gl/cogl-fbo.h: + * clutter/cogl/gl/cogl-fbo.c: + * clutter/cogl/gles/cogl-fbo.h: + * clutter/cogl/gles/cogl-fbo.c: + * clutter/cogl/cogl.h: + Added reference counting functions + + * clutter/clutter-texture.c (struct _ClutterTexturePrivate): + max_tile_waste is now guint instead of gint. + (clutter_texture_realize): Added a call to + cogl_texture_set_filters for every new texture created. + (clutter_texture_set_property): Calls + clutter_texture_set_max_tile_waste to set the PROP_MAX_TILE_WASTE + property. + (clutter_texture_get_property): Calls getter functions for the + PROP_MAX_TILE_WASTE and PROP_FILTER_QUALITY functions. + (clutter_texture_save_to_local_data): Store the filter quality and + max_tile_waste when making a local copy of the texture so that it + can be restored. + (clutter_texture_set_filter_quality): Set the filter quality on + the CoglHandle instead if it is available. + (clutter_texture_get_filter_quality): Gets the filter quality on + the CoglHandle instead if it is available. + (clutter_texture_set_cogl_texture) + (clutter_texture_set_max_tile_waste) + (clutter_texture_get_max_tile_waste): New functions + (clutter_set_cogl_texture): New public function to replace + clutter_texture_set_from_cogl_texture + + * tests/test-cogl-tex-convert.c: + * tests/test-cogl-tex-getset.c: + * tests/test-cogl-tex-tile.c: + * tests/test-cogl-offscreen.c: + * clutter/pango/pangoclutter-render.c (tc_clear): + Replace cogl_texture_destroy with cogl_texture_unref. + +2008-04-15 Ivan Leben + + * clutter/cogl/gl/cogl.c: + * clutter/cogl/gl/cogl-context.c: + * clutter/cogl/gl/cogl-context.h: + _cogl_features_init obtains pointers to all the shader related + functions and stores them into context object (same as for FBOs). + All the cogl thin wrappers around shader functions redirected + to function pointers from context. + +2008-04-14 Ivan Leben + + * clutter/cogl/cogl.h: + * clutter/cogl/gl(es)/cogl-texture.c: + New functions: cogl_texture_is_sliced and + cogl_texture_get_gl_texture. + +2008-04-09 Ivan Leben + + * cluter/cogl/gl/cogl-texture.c: + * cluter/cogl/gles/cogl-texture.c: + Manual texture coordinate (de)normalization to avoid + per-slice texture matrix push/pop. + +2008-04-08 Neil Roberts + + * clutter/clutter-texture.c (clutter_texture_save_to_local_data): + Got rid of COGL_PIXEL_FORMAT_HAS_ALPHA now that CoglPixelFormat + has the COGL_A_BIT flag to signify that the format has an alpha + channel. + +2008-04-08 Ivan Leben + + * clutter/cogl/*: + cogl_enable is now internal. All the cogl primitive and + texture drawing functions automatically enable required + flags. + + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gles/cogl-texture.c: + Improved texture drawing to optimize for special cases. + Coupled with internal enable flag caching, the font + performance has now been restored. + + * clutter/cogl/gl/cogl-defines.h: + * clutter/cogl/gles/cogl-defines.h: + Clean-up of obsolete defines. One step closer to not + having it included in cogl.h + + * clutter/glx/clutter-glx-texture-pixmap.c: + commented usage of old cogl functions + + * tests/test-text.c: + fps counter for measuring texture performance + +2008-04-07 Neil Roberts + + Removed GdkPixbuf related code from ClutterTexture. + + * clutter/cogl/gles/cogl-texture.c (cogl_texture_new_from_file): + * clutter/cogl/gl/cogl-texture.c (cogl_texture_new_from_file): + * clutter/cogl/common/cogl-bitmap-pixbuf.c + (_cogl_bitmap_from_file): + Now takes an error pointer to pass up errors from gdk-pixbuf. + + * clutter/clutter-texture.h: Removed pixbuf functions and added + clutter_texture_set_from_file and clutter_texture_new_from_file. + + * clutter/clutter-texture.c (clutter_texture_unrealize, + clutter_texture_realize): Now saves the system memory copy of the + texture to a local guchar array instead of to a pixbuf + (clutter_texture_set_property, clutter_texture_get_property): + Removed the pixbuf property. + (clutter_texture_get_pixbuf, clutter_texture_set_pixbuf): + Removed. + (clutter_texture_set_from_file): New function. + + * tests/test-viewport.c: + * tests/test-textures.c: + * tests/test-shader.c: + * tests/test-rotate.c: + * tests/test-fbo.c: + * tests/test-depth.c: + * tests/test-cogl-tex-tile.c: + * tests/test-cogl-tex-getset.c: + * tests/test-cogl-tex-convert.c: + * tests/test-cogl-offscreen.c: + * tests/test-behave.c: + * tests/test-actors.c: + Adapted to use clutter_texture_new_from_file instead of + clutter_texture_new_from_pixbuf. + +2008-04-07 Neil Roberts + + Adapted PangoClutterRenderer to use the new COGL API. This + currently seems to get about half the performance though. + + * clutter/pango/pangoclutter-render.c: Now uses CoglHandles and + the new texture API instead of creating GL textures directly. + (draw_trapezoid): Removed as it doesn't seem to be used anywhere. + + * clutter/cogl/gles/cogl-texture.c (_cogl_pixel_format_to_gl): + Added the COGL_PIXEL_FORMAT_A8 pixel format. + (_cogl_texture_upload_subregion_to_gl): Now sets + GL_UNPACK_ALIGNMENT based on the rowstride otherwise it won't + support bitmaps aligned to anything but 4. + + * clutter/cogl/gl/cogl-texture.c (_cogl_pixel_format_to_gl): Added + the COGL_PIXEL_FORMAT_A8 pixel format. + +2008-04-04 Neil Roberts + + * clutter/cogl/gles/cogl-texture.c (_cogl_texture_upload_to_gl): + Set GL_UNPACK_ALIGNMENT to 1 before uploading data for a tile. The + default value is 4 so if you used image data that doesn't have an + alpha it would previously be misaligned and show a corrupted + texture. + +2008-04-04 Neil Roberts + + * clutter/cogl/gl/cogl-texture.c (_cogl_subregion_gl_store_rules): + Now sets ROW_LENGTH based on the row stride instead of the image + width. This allows you to use the row stride to make a texture + from a sub-section of a larger image. This is necessary to use + GdkPixbufs generated using gdk_pixbuf_new_subpixbuf. + + * clutter/clutter-texture.c (clutter_texture_set_from_data): Fixed + a bug with sync-size if you try to change the size of the texture + data more than once. When clutter_actor_set_size is called in + response to the texture size being changed the sync_actor_size + flag is cleared. The fix just sets it again after the call is + finished. + +2008-04-03 Ivan Leben + + * clutter/cogl/cogl.h: + * clutter/cogl/gl/cogl.c: + * clutter/cogl/gles/cogl.c: + Removed old offscreen api. Renamed cogl_offscreen_buf_destroy + to cogl_offscreen_destroy since there is no clash anymore. + + * clutter/cogl/gl/fbo.c: + * clutter/cogl/gles/fbo.c: + * clutter/clutter-texture.c: + * tests/test-cogl-offscreen.c: + cogl_offscreen_buf_destroy -> cogl_offscreen_destroy + +2008-04-03 Ivan Leben + + * clutter/clutter-texture.c: Fixed an = to |= typo. + +2008-04-03 Neil Roberts + + * tests/test-textures.c (main): Removed call to + clutter_texture_get_n_tiles which is no longer supported. + +2008-04-03 Neil Roberts + + Upgraded ClutterTexture and ClutterCloneTexture to use the new + texture API in COGL. + + * clutter/clutter-texture.c (clutter_texture_realize): Greatly + simplified to just load the texture data using the new COGL + API. The static utility functions that were used to load the data + have been removed and are now in COGL. + (clutter_texture_set_property): Removed the use-tiles property + because it can no longer be implemented with the new COGL API. The + repeat-x, repeat-y and filter-quality properties now cause the + actor to be redrawn. + (clutter_texture_get_property): Removed the pixel-type property + because the information is now available in the single + pixel-format property. This is now directly queried from the COGL + texture instead of being stored in a private member variable. + + * clutter/clutter-texture.h: Removed API functions that are + related to tiling because they are no longer neccessary to + implement ClutterCloneTexture using the new COGL texture API. + + * clutter/clutter-clone-texture.c (clutter_clone_texture_set_property) + (clutter_clone_texture_get_property): Added repeat-x and repeat-y + properties. + (clone_texture_render_to_gl_quad): Removed so that the painting + can be done via the COGL API instead. + (clutter_clone_texture_paint): Painting greatly simplified so that + it just gets a handle to the COGL texture from the parent + ClutterTexture and calls COGL to render it. Also supports the + repeat-x and repeat-y properties by extending the texture + co-ordinates given to cogl_texture_rectangle. + +2008-04-02 Ivan Leben + + * clutter/cogl/gl/cogl-texture.c: + Fixed a bug where cogl_texture_get_data would + corrupt memory if the texture was sliced. + +2008-04-02 Ivan Leben + + * clutter/cogl/cogl.h: + * clutter/cogl/gl(es)/cogl-texture.h: + * clutter/cogl/gl(es)/cogl-texture.c: + + Added min/mag filter property to a texture. + New function cogl_texture_set_filters. + + cogl_texture_get_properties broken into separate + functions for each of the texture properties + (e.g. cogl_texture_get_width). + + * clutter/clutter-texture.c: + * clutter/glx/clutter-glx-texture-pixmap.c: + * clutter/pango/pangoclutter-render.c: + + Commented out clashes with new api while Clutter + is being moved to new textures api. + + * tests/test-cogl-tex-tile.c: + Set linear min mag filters after creating texture. + + * tests/test-cogl-tex-getset.c: + Use new getter functions. + +2008-04-02 Ivan Leben + + * clutter/cogl/gl/cogl.c: + * clutter/cogl/gles/cogl.c: + Explicit cast to remove const qualifier when initing + bitmap structure with given data argument fixes the + compile warning. + +2008-04-02 Ivan Leben + + * clutter/cogl/cogl.h: + * clutter/cogl/gl/cogl.c: + Rearraging, grouping and marking of functions that + are expected to be removed in the cleanup phase + after clutter is moved to new api. + + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gles/cogl-texture.c: + * tests/test-cogl-tex-getset.c: + Renamed cogl_texture_set_subregion to + cogl_texture_set_region. Data argument made const. + +2008-03-31 Ivan Leben + + * cluter/cogl/gl/cogl-context.c: + Removed the inclusion of gles header. (Copy-paste + error when syncing with gles port.) + +2008-03-26 Ivan Leben + + * tests: update svn:ignore + +2008-03-26 Ivan Leben + + Bug fixes, code rearrangement. + FBOs ported to GLES (mainly just stubs). + + * clutter/cogl/gl/cogl-texture.c: + New function cogl_texture_new_with_size creates + an empty texture of given size. + + * tests/test-cogl-offscreen.c: + Two partially-occluded rectangles are drawn + to an offscreen buffer and then blended as + one entity with the textured background. + +2008-03-20 Ivan Leben + + * clutter/cogl/cogl.h: + * clutter/cogl/cogl.c: + * clutter/cogl/cogl-context.h: + * clutter/cogl/cogl-context.c: + + Added COGL_FEATURE_OFFSCREEN_BLIT and + COGL_FEATURE_OFFSCREEN_MULTISAMPLE. + + Added function pointers for generation + of renderbuffers. + + All EXT function pointers now stored in + context structure + + A default context structure is now created + automatically upon a call to + cogl_context_get_default + + * tests/test-cogl-xxx.c: + + Removed a call to cogl_context_create. + + * clutter/cogl/gl/cogl-defines.h: + + Removed EXT fuction declarations. We rely on + glext.h to provied those anyway. + + * clutter/cogl/gl/cogl-fbo.c: + + Checks for offscreen feature support before + calling EXT functions via pointers in context. + +2008-03-20 Ivan Leben + + * clutter/cogl/gl(es)/cogl-texture.c: + Moved some declarations from here ... + + * clutter/cogl/gl(es)/cogl-texture.h: + ...to a new header file, to be able to reference + textures from FBOs. + + * clutter/cogl/gl(es)/cogl-context.h: + * clutter/cogl/gl(es)/cogl-context.c: + * clutter/cogl/gl/cogl-fbo.h: + * clutter/cogl/gl/cogl-fbo.c: + Began implementing FBOs. GL flavour currently + not compilable. Need to resolve some issues + in cogl-defines.h. + +2008-03-18 Ivan Leben + + * clutter/cogl/gl(es)/cogl-context.h: + * clutter/cogl/gl(es)/cogl-context.c: + Implemented a CoglContext structure and context + create / destroy api to be able to init state + properly and centralize state variables. + + * clutter/cogl/gl(es)/cogl-primitives.h: + * clutter/cogl/gl(es)/cogl-primitives.c: + * clutter/cogl/gl(es)/cogl-texture.h: + * clutter/cogl/gl(es)/cogl-texture.c: + Moved state variables to CoglContext. + + * tests/test-cogl-*.c: + Updated CoglTestbox to create cogl context. + + * clutter/eglx/clutter-stage-egl.c: + Some more testing with glGet values. + +2008-03-11 Ivan Leben + + * clutter/eglx/clutter-stage-egl.c: + Output all the available configurations prior to + choosing one. Clearly shows no stencil buffer + available with PowerVR's GLES SDK. + +2008-03-07 Ivan Leben + + * clutter/cogl/common/cogl-bitmap.h: + * clutter/cogl/common/cogl-bitmap.c: + Utility function to copy data subregion between + two bitmaps of same format. + + * clutter/cogl/gl/cogl-texture.c: + Minor bug fix. + + * clutter/cogl/gles/cogl-texture.c: + Texture api ported to GLES (get/set still missing). + + * clutter/cogl/cogl.h: + Texture creation functions now also take maximum + waste parameter. + + * tests/test-cogl-tex-xxx.c: + Update to new texture creation api. + +2008-03-06 Ivan Leben + + * ./ : update svn:ignore + * tests: update svn:ignore + +2008-03-06 Ivan Leben + + * clutter/cogl/common: + Common files (util and bitmap related) moved here + and compiled into an intermediate static lib to be + linked in to proper gl(es) version of the library. + +2008-03-04 Ivan Leben + + * clutter/cogl/cogl.h: + * clutter/cogl/gl/cogl-texture.c: + cogl_texture_set_subregion implemented + + * tests/test-cogl-tex-getset.c: + Tests texture image retrieval and subregion update + api. + +2008-03-01 Ivan Leben + + * tests/test-cogl.c: + Renamed to: + + * tests/test-cogl-primitives.c: + ... and reverted to test the primitives api + + * tests/test-cogl-tex-tile.c: + A separate test for texture tiling. Now animated to + test a broader range of input data. + + * tests/test-cogl-tex-convert.c: + Tests bitmap conversion functions by forcing the image + data to a different internal format. + +2008-02-29 Ivan Leben + + * clutter/cogl/gl/cogl-bitmap.h: + * clutter/cogl/gl/cogl-bitmap-fallback.h: + * clutter/cogl/gl/cogl-bitmap-pixbuf.h: + Many bugs fixed in the fallback conversion code that + make it actually work. Conversions from rgba to other + 24 and 32 bit formats required by cogl_texture_get_image. + + * clutter/cogl/cogl.h: + * clutter/cogl/gl/cogl-texture.c: + Bug fixes to make conversions work. cogl_texture_new_xxx + functions now take internal format as a convenience so + cogl can do a conversion prior to passing the data to + GL if requested so by the user (e.g. rgb(a) -> grayscale) + +2008-02-28 Ivan Leben + + * clutter/cogl/gl/cogl.c: + Moved _cogl_get_format_bpp ... + + * clutter/cogl/gl/cogl-bitmap.c: + - ... here + - Convenience function _cogl_bitmap_convert_and_premult + does both + + * clutter/cogl/gl/cogl-texture.c: + - Generalized the gl pixel storage setup function to + be able to specify both pack and unpack rules + - implemented _cogl_texture_download_from_gl + - implemented cogl_texture_get_image (renamed from + cogl_texture_get_subregion, since obtaining subregion + is not supported by GL.. might get renamed again :) + +2008-02-27 Ivan Leben + + * clutter/cogl/cogl.h: + * clutter/cogl/gl/cogl-texture.c: + + - New function cogl_texture_get_properties returns + texture width, height and maximum waste. + + - CoglSpanIter structure and iterator api abstracts + iteration over slices when an area needs to be + covered by returning slice-coverage intersection + info. + + - Software tiling simplified using span iterators + + - Began work on cogl_texture_get_sub_region and + cogl_texture_set_sub_region (will use span iterators + as well) + +2008-02-27 Ivan Leben + + * tests/test-cogl.c: + Background color changed to emphasize the transparency + in the redhand image. + +2008-02-27 Ivan Leben + + * clutter/cogl/cogl.h: + cogl_texture_draw renamed to cogl_texture_rectangle to + better match the rest of the drawing api + + * clutter/cogl/gl/cogl-texture.c: + Harware tiling implemented and used when supported. + Software (manual) tiling now only used as a fallback. + +2008-02-27 Ivan Leben + + * clutter/cogl/gl(es)/cogl.c: + Moved the COGL_DEBUG definition from source ... + + * clutter/cogl/gl(es)/cogl-internal.h: + ... to header file. + + * clutter/cogl/gl/cogl-texture.c: + - Uploading to gl for multiple slices implemented + - Unique upload function for both one and many slices + - Some fixes for bugs found while testing + - Some more comments + +2008-02-26 Ivan Leben + + * clutter/cogl/cogl.h: + Texture drawing api now takes texture coordinates + for quad corners. + + * clutter/cogl/gl/cogl-texture.c: + Tiling has been implemented. Whenever the quad texture + corner coordinates are different than (0,0)-(1,1) the + texture slices are automatically drawn as many times + as to cover the whole quad area, thus tiling the whole + texture image across the quad. To be used as a fallback + where GL_REPEAT for texture wrap mode is not supported. + + * clutter/tests/test-cogl.c: + Draws the red hand image tiled several times. + +2008-02-23 Ivan Leben + + * clutter/cogl/gl/cogl-texture.c: + slicing moved into cogl texture api + +2008-02-22 Ivan Leben + + * clutter/cogl/TODO: + a special (temporary) TODO for cogl overhaul related + stuff + +2008-02-22 Ivan Leben + + * clutter/clutter-feature.h: + added a new feature: CLUTTER_FEATURE_TEXTURE_NPOT + + * clutter/cogl/cogl.h: + cogl has got its own feature flags defined + + * clutter/cogl/gl(es)/cogl.c: + new feature check for ARB_texture_non_power_of_two + + * clutter/cogl/gl(es)/cogl-util.h: + * clutter/cogl/gl(es)/cogl-util.c: + cogl_util_next_p2 moved from clutter for independency + + * clutter/cogl/gl/cogl-texture.c: + - texture handles being stored into and array + - cogl_is_texture checks for a valid handle + - structures and functions prepared for implementation + of slicing + +2008-02-21 Ivan Leben + + * clutter/cogl/gl/cogl.c: + * clutter/cogl/gl/cogl-internal.h: + * clutter/cogl/gl/cogl-bitmap-pixbuf.c: + * clutter/cogl/gl/cogl-bitmap-fallback.c: + * clutter/cogl/gl/cogl-texture.c: + Some debugging of the texture code written so far... + + * clutter/tests/test-cogl.c: + ...and the first texture is being drawn using the new api! + +2008-02-19 Ivan Leben + + * clutter/cogl/gl/cogl.h: + braces () around complex pixel format enums + + * clutter/cogl/gl/cogl-bitmap.h: + * clutter/cogl/gl/cogl-bitmap-fallback.h: + * clutter/cogl/gl/cogl-bitmap-pixbuf.h: + simplified api by introducing internal CoglBitmap struct + + * clutter/cogl/gl/cogl-texture.c: + - using CoglBitmap + - format conversion and alpha unpremultiplication using + cogl_bitmap_xxx api + +2008-02-19 Ivan Leben + + * clutter/cogl/gl/cogl-texture.c: + prepared to implement the upload-to-GL stage + +2008-02-19 Ivan Leben + + * clutter/cogl/gl/cogl-texture.c: + loading from file (first stage - load data into memory) + implemented in CoglTexture + + * clutter/cogl/gl/cogl-bitmap.h: + * clutter/cogl/gl/cogl-bitmap-fallback.c: + * clutter/cogl/gl/cogl-bitmap-pixbuf.c: + loading from file implemented in GdkPixbuf imaging backend + +2008-02-18 Ivan Leben + + * clutter/cogl/gl/cogl-texture.c: + * clutter/cogl/gl/cogl-bitmap.h: + * clutter/cogl/gl/cogl-bitmap-fallback.c: + * clutter/cogl/gl/cogl-bitmap-pixbuf.c: + image loading and pixel conversions moved behind a simple + abstraction api that makes adding new native image library + 'backends' easy + + * clutter/cogl/gl/Makefile.am: + for now hardcoded to use pixbuf imaging library, this + should be handled by configure.ac later + + * clutter/cogl/cogl.h: + updated CoglFormat enum to add premultiplied image formats + +2008-02-16 Ivan Leben + + * clutter/cogl/gl/cogl-texture.c: started moving + clutter-texture code to cogl level + + * clutter/cogl/cogl.h: enums and declarations required + by the textures api (not complete yet) + +2008-02-13 Ivan Leben + + * clutter/cogl/gl/cogl-primitives.c: + * clutter/cogl/gles/cogl-primitives.c: + Primitives api extensively commented. + +2008-02-13 Ivan Leben + + * clutter/cogl/gl/cogl-primitives.c: moved cogl_color ... + + * clutter/cogl/gl/cogl.c: ... back here, since it might be + subject to more general use. + + * clutter/cogl/gles/cogl.c: + * clutter/cogl/gles/cogl-internal.h: + * clutter/cogl/gles/cogl-primitives.c: + Primitives api ported to GLES. + +2008-02-12 Ivan Leben + + * clutter/cogl/cogl.c: moved drawing api... + + * clutter/cogl/cogl-primitives.c: ...to a separate file + + * clutter/cogl/cogl-internal.h: hosts internal declarations + and #defines common to all the source files implementing + cogl + +2008-02-12 Ivan Leben + + * clutter/cogl/cogl.c: + + - removed cogl_set_fill_color, cogl_set_stroke_color + (a unique color is specifiad via cogl_color and used + for both filling and stroking) + + - renamed cogl_rectangle to cogl_fast_fill_rectangle + (uses current color just like cogl_fill and cogl_stroke) + + - renamed cogl_trapezoid to cogl_fast_fill_trapezoid + (uses current color just like cogl_fill and cogl_stroke) + + - cogl_fast_fill_rectanglex, cogl_fast_fill_trapezoidx + (ClutterFixed versions) + + * clutter/cogl/cogl.h: + rectangle and color api update + + * tests/test-coglc: + update to use cogl_color instead + + * clutter/clutter-actor.c: + * clutter/clutter-rectangle.c: + cogl_rectangle => cogl_fast_fill_rectangle + + * clutter/pango/pangoclutter-render.c: + cogl_trapezoid => cogl_fast_fill_trapezoid + +2008-02-12 Ivan Leben + + * clutter/cogl/cogl.h: drawing api inserted into header + + * clutter/cogl/gl/cogl.c: + - bezier relative functions + - internal functions made static + +2008-02-12 Ivan Leben + + * clutter/cogl/gl/cogl.c: + - relative versions of path_xxx_to apis (path_xxx_to_rel) + - horizontal, vertical lines for even more handyness + - cogl_round_rectangle + - all the input arguments changed to ClutterFixed + + * tests/test-cogl.c: added round rectangle to drawing list + +2008-02-11 Ivan Leben + + * clutter/cogl/gl/cogl.c: bezier quadratic and cubic curve + subdivision implemented. + + * tests/test-cogl.c: added bezier curves to drawing list + +2008-02-09 Ivan Leben + + * tests/test-cogl.c: moved the "cogltester" class from + clutter-cogltester.c into a dedicated test app. The cogl testing + actor cycles between and draws various cogl primitives. + + * clutter/cogl/gl/cogl.c: almost all the basic primitives done + +2008-02-08 Ivan Leben + + * clutter/cogl/gl/cogl.c: some more work on higher level drawing api + + * clutter/clutter-cogltester.h: header for a simple actor to test new + cogl api. For now just an empty rig. + + * clutter/clutter-cogltester.c: implementation of a simple actor to + test new cogl api. For now just an empty rig. + +2008-02-07 Ivan Leben + + * clutter/cogl/gl/cogl.c: started work on higher level drawing api. + diff --git a/clutter/Makefile.am b/clutter/Makefile.am index 96413483f..704520389 100644 --- a/clutter/Makefile.am +++ b/clutter/Makefile.am @@ -22,9 +22,9 @@ GLIB_MKENUMS=`pkg-config --variable=glib_mkenums glib-2.0` INCLUDES = \ -I$(top_srcdir) \ -I$(top_srcdir)/clutter/pango \ - -I$(top_srcdir)/clutter/cogl \ - -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ + -I$(top_srcdir)/clutter \ $(clutter_json_include) \ + -I$(top_builddir)/clutter \ -DCLUTTER_PREFIX=\""$(prefix)"\" \ -DCLUTTER_LIBDIR=\""$(libdir)"\" \ -DCLUTTER_DATADIR=\""$(datadir)"\" \ @@ -87,7 +87,7 @@ source_h = \ $(srcdir)/clutter-types.h \ $(srcdir)/clutter-units.h \ $(srcdir)/clutter-util.h \ - $(srcdir)/clutter-version.h \ + $(top_builddir)/clutter/clutter-version.h \ $(NULL) clutter-marshal.h: stamp-clutter-marshal.h @@ -113,8 +113,7 @@ clutter-marshal.c: clutter-marshal.h Makefile clutter-enum-types.h: stamp-clutter-enum-types.h @true stamp-clutter-enum-types.h: $(source_h) Makefile - ( cd $(srcdir) && \ - $(GLIB_MKENUMS) \ + ( $(GLIB_MKENUMS) \ --template $(srcdir)/clutter-enum-types.h.in \ $(source_h) ) >> xgen-ceth && \ (cmp xgen-ceth clutter-enum-types.h || cp xgen-ceth clutter-enum-types.h) && \ @@ -122,8 +121,7 @@ stamp-clutter-enum-types.h: $(source_h) Makefile echo timestamp > $(@F) clutter-enum-types.c: clutter-enum-types.h - ( cd $(srcdir) && \ - $(GLIB_MKENUMS) \ + ( $(GLIB_MKENUMS) \ --template $(srcdir)/clutter-enum-types.c.in \ $(source_h) ) >> xgen-cetc && \ cp xgen-cetc clutter-enum-types.c && \ @@ -144,9 +142,9 @@ source_c = \ clutter-behaviour-path.c \ clutter-behaviour-rotate.c \ clutter-behaviour-scale.c \ + clutter-clone-texture.c \ clutter-color.c \ clutter-container.c \ - clutter-clone-texture.c \ clutter-effect.c \ clutter-entry.c \ clutter-enum-types.c \ diff --git a/clutter/clutter-actor.c b/clutter/clutter-actor.c index b7e9e37da..6ca85ace5 100644 --- a/clutter/clutter-actor.c +++ b/clutter/clutter-actor.c @@ -149,7 +149,7 @@ #include "clutter-private.h" #include "clutter-debug.h" #include "clutter-units.h" -#include "cogl.h" +#include "cogl/cogl.h" typedef struct _ShaderData ShaderData; @@ -488,10 +488,10 @@ clutter_actor_real_pick (ClutterActor *self, if (clutter_actor_should_pick_paint (self)) { cogl_color (color); - cogl_rectangle (0, - 0, - clutter_actor_get_width(self), - clutter_actor_get_height(self)); + cogl_fast_fill_rectangle (0, + 0, + clutter_actor_get_width(self), + clutter_actor_get_height(self)); } } @@ -3762,7 +3762,6 @@ clutter_actor_set_rotationu (ClutterActor *self, x, y, z); } - /** * clutter_actor_set_rotationx: * @self: a #ClutterActor @@ -5255,6 +5254,8 @@ clutter_actor_set_custom_property (ClutterScriptable *scriptable, const gchar *name, const GValue *value) { + CLUTTER_NOTE (SCRIPT, "in ClutterActor::set_custom_property('%s')", name); + if (strcmp (name, "rotation") == 0) { RotationInfo *info; diff --git a/clutter/clutter-clone-texture.c b/clutter/clutter-clone-texture.c index 7a1de598a..58cd7eb6f 100644 --- a/clutter/clutter-clone-texture.c +++ b/clutter/clutter-clone-texture.c @@ -44,12 +44,14 @@ #include "clutter-private.h" #include "clutter-debug.h" -#include "cogl.h" +#include "cogl/cogl.h" enum { PROP_0, - PROP_PARENT_TEXTURE + PROP_PARENT_TEXTURE, + PROP_REPEAT_Y, + PROP_REPEAT_X }; G_DEFINE_TYPE (ClutterCloneTexture, @@ -62,122 +64,20 @@ G_DEFINE_TYPE (ClutterCloneTexture, struct _ClutterCloneTexturePrivate { ClutterTexture *parent_texture; + guint repeat_x : 1; + guint repeat_y : 1; }; -static void -clone_texture_render_to_gl_quad (ClutterCloneTexture *ctexture, - int x_1, - int y_1, - int x_2, - int y_2) -{ - gint qx1 = 0, qx2 = 0, qy1 = 0, qy2 = 0; - gint qwidth = 0, qheight = 0; - gint x, y, i = 0, lastx = 0, lasty = 0; - gint n_x_tiles, n_y_tiles; - gint pwidth, pheight; - float tx, ty; - - ClutterCloneTexturePrivate *priv = ctexture->priv; - ClutterActor *parent_actor = CLUTTER_ACTOR (priv->parent_texture); - - priv = ctexture->priv; - - qwidth = x_2 - x_1; - qheight = y_2 - y_1; - - if (!CLUTTER_ACTOR_IS_REALIZED (parent_actor)) - clutter_actor_realize (parent_actor); - - /* Only paint if parent is in a state to do so */ - if (!clutter_texture_has_generated_tiles (priv->parent_texture)) - return; - - clutter_texture_get_base_size (priv->parent_texture, &pwidth, &pheight); - - if (!clutter_texture_is_tiled (priv->parent_texture)) - { - clutter_texture_bind_tile (priv->parent_texture, 0); - - /* NPOTS textures *always* used if extension available - */ - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE)) - { - tx = (float) pwidth; - ty = (float) pheight; - } - else - { - tx = (float) pwidth / clutter_util_next_p2 (pwidth); - ty = (float) pheight / clutter_util_next_p2 (pheight); - } - - cogl_texture_quad (x_1, x_2, y_1, y_2, - 0, - 0, - CLUTTER_FLOAT_TO_FIXED (tx), - CLUTTER_FLOAT_TO_FIXED (ty)); - return; - } - - clutter_texture_get_n_tiles (priv->parent_texture, &n_x_tiles, &n_y_tiles); - - for (x = 0; x < n_x_tiles; x++) - { - lasty = 0; - - for (y = 0; y < n_y_tiles; y++) - { - gint actual_w, actual_h; - gint xpos, ypos, xsize, ysize, ywaste, xwaste; - - clutter_texture_bind_tile (priv->parent_texture, i); - - clutter_texture_get_x_tile_detail (priv->parent_texture, - x, &xpos, &xsize, &xwaste); - - clutter_texture_get_y_tile_detail (priv->parent_texture, - y, &ypos, &ysize, &ywaste); - - actual_w = xsize - xwaste; - actual_h = ysize - ywaste; - - tx = (float) actual_w / xsize; - ty = (float) actual_h / ysize; - - qx1 = x_1 + lastx; - qx2 = qx1 + ((qwidth * actual_w ) / pwidth ); - - qy1 = y_1 + lasty; - qy2 = qy1 + ((qheight * actual_h) / pheight ); - - CLUTTER_NOTE (TEXTURE, - "rendering text tile x: %i, y: %i - %ix%i", - x, y, - actual_w, actual_h); - - cogl_texture_quad (qx1, qx2, qy1, qy2, - 0, - 0, - CLUTTER_FLOAT_TO_FIXED (tx), - CLUTTER_FLOAT_TO_FIXED (ty)); - - lasty += qy2 - qy1; - - i++; - } - lastx += qx2 - qx1; - } -} - static void clutter_clone_texture_paint (ClutterActor *self) { ClutterCloneTexturePrivate *priv; ClutterActor *parent_texture; gint x_1, y_1, x_2, y_2; - GLenum target_type; ClutterColor col = { 0xff, 0xff, 0xff, 0xff }; + CoglHandle cogl_texture; + ClutterFixed t_w, t_h; + guint tex_width, tex_height; priv = CLUTTER_CLONE_TEXTURE (self)->priv; @@ -199,20 +99,6 @@ clutter_clone_texture_paint (ClutterActor *self) cogl_push_matrix (); - /* FIXME: figure out nicer way of getting at this info... - */ - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE) && - clutter_texture_is_tiled (CLUTTER_TEXTURE (parent_texture)) == FALSE) - { - target_type = CGL_TEXTURE_RECTANGLE_ARB; - cogl_enable (CGL_ENABLE_TEXTURE_RECT|CGL_ENABLE_BLEND); - } - else - { - target_type = CGL_TEXTURE_2D; - cogl_enable (CGL_ENABLE_TEXTURE_2D|CGL_ENABLE_BLEND); - } - col.alpha = clutter_actor_get_abs_opacity (self); cogl_color (&col); @@ -223,9 +109,30 @@ clutter_clone_texture_paint (ClutterActor *self) x_1, y_1, x_2, y_2, clutter_actor_get_opacity (self)); + cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture); + + if (cogl_texture == COGL_INVALID_HANDLE) + return; + + tex_width = cogl_texture_get_width (cogl_texture); + tex_height = cogl_texture_get_height (cogl_texture); + + if (priv->repeat_x && tex_width > 0) + t_w = CFX_QDIV (CLUTTER_INT_TO_FIXED (x_2 - x_1), + CLUTTER_INT_TO_FIXED (tex_width)); + else + t_w = CFX_ONE; + if (priv->repeat_y && tex_height > 0) + t_h = CFX_QDIV (CLUTTER_INT_TO_FIXED (y_2 - y_1), + CLUTTER_INT_TO_FIXED (tex_height)); + else + t_h = CFX_ONE; + /* Parent paint translated us into position */ - clone_texture_render_to_gl_quad (CLUTTER_CLONE_TEXTURE (self), - 0, 0, x_2 - x_1, y_2 - y_1); + cogl_texture_rectangle (cogl_texture, 0, 0, + CLUTTER_INT_TO_FIXED (x_2 - x_1), + CLUTTER_INT_TO_FIXED (y_2 - y_1), + 0, 0, t_w, t_h); cogl_pop_matrix (); } @@ -294,13 +201,30 @@ clutter_clone_texture_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - ClutterCloneTexture *ctexture = CLUTTER_CLONE_TEXTURE (object); + ClutterCloneTexture *ctexture = CLUTTER_CLONE_TEXTURE (object); + ClutterCloneTexturePrivate *priv; + + priv = ctexture->priv; switch (prop_id) { case PROP_PARENT_TEXTURE: set_parent_texture (ctexture, g_value_get_object (value)); break; + case PROP_REPEAT_X: + if (priv->repeat_x != g_value_get_boolean (value)) + { + priv->repeat_x = !priv->repeat_x; + clutter_actor_queue_redraw (CLUTTER_ACTOR (ctexture)); + } + break; + case PROP_REPEAT_Y: + if (priv->repeat_y != g_value_get_boolean (value)) + { + priv->repeat_y = !priv->repeat_y; + clutter_actor_queue_redraw (CLUTTER_ACTOR (ctexture)); + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -314,12 +238,21 @@ clutter_clone_texture_get_property (GObject *object, GParamSpec *pspec) { ClutterCloneTexture *ctexture = CLUTTER_CLONE_TEXTURE (object); + ClutterCloneTexturePrivate *priv; + + priv = ctexture->priv; switch (prop_id) { case PROP_PARENT_TEXTURE: g_value_set_object (value, ctexture->priv->parent_texture); break; + case PROP_REPEAT_X: + g_value_set_boolean (value, priv->repeat_x); + break; + case PROP_REPEAT_Y: + g_value_set_boolean (value, priv->repeat_y); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -339,13 +272,31 @@ clutter_clone_texture_class_init (ClutterCloneTextureClass *klass) gobject_class->set_property = clutter_clone_texture_set_property; gobject_class->get_property = clutter_clone_texture_get_property; - g_object_class_install_property (gobject_class, - PROP_PARENT_TEXTURE, - g_param_spec_object ("parent-texture", - "Parent Texture", - "The parent texture to clone", - CLUTTER_TYPE_TEXTURE, - CLUTTER_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, PROP_PARENT_TEXTURE, + g_param_spec_object ("parent-texture", + "Parent Texture", + "The parent texture to clone", + CLUTTER_TYPE_TEXTURE, + CLUTTER_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, PROP_REPEAT_X, + g_param_spec_boolean ("repeat-x", + "Tile underlying pixbuf in x direction", + "Reapeat underlying pixbuf rather than scale " + "in x direction.", + FALSE, + CLUTTER_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, PROP_REPEAT_Y, + g_param_spec_boolean ("repeat-y", + "Tile underlying pixbuf in y direction", + "Reapeat underlying pixbuf rather than scale " + "in y direction.", + FALSE, + CLUTTER_PARAM_READWRITE)); g_type_class_add_private (gobject_class, sizeof (ClutterCloneTexturePrivate)); } diff --git a/clutter/clutter-deprecated.h b/clutter/clutter-deprecated.h index 80a2a9963..eb896e122 100644 --- a/clutter/clutter-deprecated.h +++ b/clutter/clutter-deprecated.h @@ -36,10 +36,12 @@ #define clutter_actor_set_scale_with_gravity clutter_actor_set_scale_with_gravity_DEPRECATED_BY_clutter_actor_set_anchor_point_from_gravity #define clutter_actor_set_scale_with_gravityx clutter_actor_set_scale_with_gravity_DEPRECATED_BY_clutter_actor_set_anchor_point_from_gravity -#define clutter_entry_set_position clutter_entry_set_position_REPLACED_BY_clutter_entry_set_cursor_position -#define clutter_entry_get_position clutter_entry_get_position_REPLACED_BY_clutter_entry_get_cursor_position +#define clutter_entry_set_position clutter_entry_set_position_REPLACED_BY_clutter_entry_set_cursor_position +#define clutter_entry_get_position clutter_entry_get_position_REPLACED_BY_clutter_entry_get_cursor_position -#define clutter_shader_bind clutter_shader_bind_REPLACED_BY_clutter_shader_compile -#define clutter_shader_is_bound clutter_shader_is_bound_REPLACED_BY_clutter_shader_is_compiled +#define clutter_shader_bind clutter_shader_bind_REPLACED_BY_clutter_shader_compile +#define clutter_shader_is_bound clutter_shader_is_bound_REPLACED_BY_clutter_shader_is_compiled + +#define clutter_texture_new_from_pixbuf clutter_texture_new_from_pixbuf_DEPRECATED_BY_clutter_texture_new_from_file_OR_clutter_texture_new_AND_clutter_texture_set_from_rgb_data #endif /* CLUTTER_DEPRECATED_H */ diff --git a/clutter/clutter-feature.c b/clutter/clutter-feature.c index a3a2883b5..10edfe994 100644 --- a/clutter/clutter-feature.c +++ b/clutter/clutter-feature.c @@ -43,7 +43,7 @@ #include "clutter-private.h" #include "clutter-debug.h" -#include "cogl.h" +#include "cogl/cogl.h" typedef struct ClutterFeatures { @@ -53,6 +53,32 @@ typedef struct ClutterFeatures static ClutterFeatures* __features = NULL; +ClutterFeatureFlags +_clutter_features_from_cogl (guint cogl_flags) +{ + ClutterFeatureFlags clutter_flags = 0; + + if (cogl_flags & COGL_FEATURE_TEXTURE_RECTANGLE) + clutter_flags |= CLUTTER_FEATURE_TEXTURE_RECTANGLE; + + if (cogl_flags & COGL_FEATURE_TEXTURE_NPOT) + clutter_flags |= CLUTTER_FEATURE_TEXTURE_NPOT; + + if (cogl_flags & COGL_FEATURE_TEXTURE_YUV) + clutter_flags |= CLUTTER_FEATURE_TEXTURE_YUV; + + if (cogl_flags & COGL_FEATURE_TEXTURE_READ_PIXELS) + clutter_flags |= CLUTTER_FEATURE_TEXTURE_READ_PIXELS; + + if (cogl_flags & COGL_FEATURE_SHADERS_GLSL) + clutter_flags |= CLUTTER_FEATURE_SHADERS_GLSL; + + if (cogl_flags & COGL_FEATURE_OFFSCREEN) + clutter_flags |= CLUTTER_FEATURE_OFFSCREEN; + + return clutter_flags; +} + void _clutter_feature_init (void) { @@ -72,8 +98,8 @@ _clutter_feature_init (void) context = clutter_context_get_default (); - __features->flags = cogl_get_features() - |_clutter_backend_get_features (context->backend); + __features->flags = (_clutter_features_from_cogl (cogl_get_features ()) + | _clutter_backend_get_features (context->backend)); __features->features_set = TRUE; diff --git a/clutter/clutter-feature.h b/clutter/clutter-feature.h index 360273271..1c94aa7d4 100644 --- a/clutter/clutter-feature.h +++ b/clutter/clutter-feature.h @@ -58,15 +58,16 @@ G_BEGIN_DECLS typedef enum { CLUTTER_FEATURE_TEXTURE_RECTANGLE = (1 << 1), - CLUTTER_FEATURE_SYNC_TO_VBLANK = (1 << 2), - CLUTTER_FEATURE_TEXTURE_YUV = (1 << 3), - CLUTTER_FEATURE_TEXTURE_READ_PIXELS = (1 << 4), - CLUTTER_FEATURE_STAGE_STATIC = (1 << 5), - CLUTTER_FEATURE_STAGE_USER_RESIZE = (1 << 6), - CLUTTER_FEATURE_STAGE_CURSOR = (1 << 7), - CLUTTER_FEATURE_SHADERS_GLSL = (1 << 8), - CLUTTER_FEATURE_OFFSCREEN = (1 << 9), - CLUTTER_FEATURE_STAGE_MULTIPLE = (1 << 10) + CLUTTER_FEATURE_TEXTURE_NPOT = (1 << 2), + CLUTTER_FEATURE_SYNC_TO_VBLANK = (1 << 3), + CLUTTER_FEATURE_TEXTURE_YUV = (1 << 4), + CLUTTER_FEATURE_TEXTURE_READ_PIXELS = (1 << 5), + CLUTTER_FEATURE_STAGE_STATIC = (1 << 6), + CLUTTER_FEATURE_STAGE_USER_RESIZE = (1 << 7), + CLUTTER_FEATURE_STAGE_CURSOR = (1 << 8), + CLUTTER_FEATURE_SHADERS_GLSL = (1 << 9), + CLUTTER_FEATURE_OFFSCREEN = (1 << 10), + CLUTTER_FEATURE_STAGE_MULTIPLE = (1 << 11) } ClutterFeatureFlags; gboolean clutter_feature_available (ClutterFeatureFlags feature); diff --git a/clutter/clutter-group.c b/clutter/clutter-group.c index 8c7b9f535..a8675530e 100644 --- a/clutter/clutter-group.c +++ b/clutter/clutter-group.c @@ -51,7 +51,7 @@ #include "clutter-marshal.h" #include "clutter-enum-types.h" -#include "cogl.h" +#include "cogl/cogl.h" enum { diff --git a/clutter/clutter-main.c b/clutter/clutter-main.c index 67d9aed8b..68853586d 100644 --- a/clutter/clutter-main.c +++ b/clutter/clutter-main.c @@ -47,7 +47,7 @@ #include "clutter-version.h" /* For flavour define */ #include "clutter-frame-source.h" -#include "cogl.h" +#include "cogl/cogl.h" /* main context */ static ClutterMainContext *ClutterCntx = NULL; @@ -247,7 +247,6 @@ _clutter_do_pick (ClutterStage *stage, clutter_maybe_setup_viewport (stage); cogl_paint_init (&white); - cogl_enable (0); /* Disable dithering (if any) when doing the painting in pick mode */ glDisable (GL_DITHER); @@ -349,12 +348,16 @@ clutter_main (void) loop = g_main_loop_new (NULL, TRUE); main_loops = g_slist_prepend (main_loops, loop); +#ifdef HAVE_CLUTTER_FRUITY_FOO + clutter_fruity_main (); +#else if (g_main_loop_is_running (main_loops->data)) { clutter_threads_leave (); g_main_loop_run (loop); clutter_threads_enter (); } +#endif main_loops = g_slist_remove (main_loops, loop); diff --git a/clutter/clutter-rectangle.c b/clutter/clutter-rectangle.c index e7d9833ad..dfc34099c 100644 --- a/clutter/clutter-rectangle.c +++ b/clutter/clutter-rectangle.c @@ -38,7 +38,7 @@ #include "clutter-private.h" #include "clutter-debug.h" -#include "cogl.h" +#include "cogl/cogl.h" G_DEFINE_TYPE (ClutterRectangle, clutter_rectangle, CLUTTER_TYPE_ACTOR); @@ -84,7 +84,7 @@ clutter_rectangle_paint (ClutterActor *self) : "unknown"); cogl_push_matrix(); - cogl_enable (CGL_ENABLE_BLEND); + //cogl_enable (CGL_ENABLE_BLEND); clutter_actor_get_geometry (self, &geom); @@ -101,22 +101,22 @@ clutter_rectangle_paint (ClutterActor *self) cogl_color (&tmp_col); /* this sucks, but it's the only way to make a border */ - cogl_rectangle (priv->border_width, 0, - geom.width - priv->border_width, - priv->border_width); + cogl_fast_fill_rectangle (priv->border_width, 0, + geom.width - priv->border_width, + priv->border_width); - cogl_rectangle (geom.width - priv->border_width, - priv->border_width, - priv->border_width, - geom.height - priv->border_width); + cogl_fast_fill_rectangle (geom.width - priv->border_width, + priv->border_width, + priv->border_width, + geom.height - priv->border_width); - cogl_rectangle (0, geom.height - priv->border_width, - geom.width - priv->border_width, - priv->border_width); + cogl_fast_fill_rectangle (0, geom.height - priv->border_width, + geom.width - priv->border_width, + priv->border_width); - cogl_rectangle (0, 0, - priv->border_width, - geom.height - priv->border_width); + cogl_fast_fill_rectangle (0, 0, + priv->border_width, + geom.height - priv->border_width); tmp_col.red = priv->color.red; tmp_col.green = priv->color.green; @@ -124,10 +124,10 @@ clutter_rectangle_paint (ClutterActor *self) cogl_color (&tmp_col); - cogl_rectangle (priv->border_width, - priv->border_width, - geom.width - priv->border_width * 2, - geom.height - priv->border_width * 2); + cogl_fast_fill_rectangle (priv->border_width, + priv->border_width, + geom.width - priv->border_width * 2, + geom.height - priv->border_width * 2); } else { @@ -138,7 +138,7 @@ clutter_rectangle_paint (ClutterActor *self) cogl_color (&tmp_col); - cogl_rectangle (0, 0, geom.width, geom.height); + cogl_fast_fill_rectangle (0, 0, geom.width, geom.height); } cogl_pop_matrix(); diff --git a/clutter/clutter-script-parser.c b/clutter/clutter-script-parser.c index 977092516..df66d0001 100644 --- a/clutter/clutter-script-parser.c +++ b/clutter/clutter-script-parser.c @@ -6,6 +6,7 @@ #include #include +#include #include "clutter-actor.h" #include "clutter-behaviour.h" diff --git a/clutter/clutter-script.c b/clutter/clutter-script.c index 503fd921c..f644d0edd 100644 --- a/clutter/clutter-script.c +++ b/clutter/clutter-script.c @@ -160,6 +160,7 @@ #include #include +#include #include "clutter-actor.h" #include "clutter-alpha.h" @@ -927,6 +928,7 @@ clutter_script_parse_node (ClutterScript *script, break; case G_TYPE_OBJECT: +#ifdef USE_GDKPIXBUF if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF)) { if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING)) @@ -958,7 +960,7 @@ clutter_script_parse_node (ClutterScript *script, g_warning ("Unable to open image at path `%s': %s", path, error->message); - g_error_free (error); + g_error_free (error); } else { @@ -983,6 +985,7 @@ clutter_script_parse_node (ClutterScript *script, } } break; +#endif default: retval = FALSE; diff --git a/clutter/clutter-shader.c b/clutter/clutter-shader.c index fcbd28ccd..21fd1bbee 100644 --- a/clutter/clutter-shader.c +++ b/clutter/clutter-shader.c @@ -49,7 +49,7 @@ #include -#include +#include "cogl/cogl.h" #include "clutter-debug.h" #include "clutter-private.h" @@ -403,9 +403,6 @@ bind_glsl_shader (ClutterShader *self, ClutterShaderPrivate *priv; priv = self->priv; - cogl_enable (CGL_FRAGMENT_SHADER); - cogl_enable (CGL_VERTEX_SHADER); - priv->program = cogl_create_program (); if (priv->vertex_is_glsl && priv->vertex_source) diff --git a/clutter/clutter-stage-window.h b/clutter/clutter-stage-window.h index d70858d38..641e8fbfb 100644 --- a/clutter/clutter-stage-window.h +++ b/clutter/clutter-stage-window.h @@ -2,7 +2,9 @@ #define __CLUTTER_STAGE_WINDOW_H__ #include +#ifdef USE_GDKPIXBUF #include +#endif G_BEGIN_DECLS @@ -28,12 +30,6 @@ struct _ClutterStageWindowIface gboolean cursor_visible); void (* set_user_resizable) (ClutterStageWindow *stage_window, gboolean is_resizable); - - GdkPixbuf * (* draw_to_pixbuf) (ClutterStageWindow *stage_window, - gint x, - gint y, - gint width, - gint height); }; GType clutter_stage_window_get_type (void) G_GNUC_CONST; diff --git a/clutter/clutter-stage.c b/clutter/clutter-stage.c index 3322124ac..4ef258d25 100644 --- a/clutter/clutter-stage.c +++ b/clutter/clutter-stage.c @@ -67,7 +67,7 @@ #include "clutter-version.h" /* For flavour */ #include "clutter-id-pool.h" -#include "cogl.h" +#include "cogl/cogl.h" G_DEFINE_TYPE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP); @@ -1012,7 +1012,7 @@ clutter_stage_hide_cursor (ClutterStage *stage) } /** - * clutter_stage_snapshot + * clutter_stage_read_pixels: * @stage: A #ClutterStage * @x: x coordinate of the first pixel that is read from stage * @y: y coordinate of the first pixel that is read from stage @@ -1021,31 +1021,65 @@ clutter_stage_hide_cursor (ClutterStage *stage) * @height: Height dimention of pixels to be read, or -1 for the * entire stage height * + * Makes a screenshot of the stage in RGBA 8bit data, returns a + * linear buffer with @width*4 as rowstride. * Gets a pixel based representation of the current rendered stage. * - * Return value: pixel representation as a #GdkPixbuf, or %NULL if - * the backend does not support this operation + * Return value: a pointer to newly allocated memory with the buffer + * that should be free with g_free, or NULL if the read back failed. */ -GdkPixbuf * -clutter_stage_snapshot (ClutterStage *stage, - gint x, - gint y, - gint width, - gint height) +guchar * +clutter_stage_read_pixels (ClutterStage *stage, + gint x, + gint y, + gint width, + gint height) { - ClutterStageWindow *impl; - ClutterStageWindowIface *iface; + guchar *pixels; + GLint viewport[4]; + gint rowstride; + gint stage_x, stage_y, stage_width, stage_height; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); + + /* according to glReadPixels documentation pixels outside the viewport are + * undefined, but no error should be provoked, thus this is probably unnneed. + */ g_return_val_if_fail (x >= 0 && y >= 0, NULL); - impl = CLUTTER_STAGE_WINDOW (stage->priv->impl); - iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl); + /* Force a redraw of the stage before reading back pixels */ + clutter_stage_paint (CLUTTER_ACTOR (stage)); + clutter_stage_ensure_current (stage); - if (iface->draw_to_pixbuf) - return iface->draw_to_pixbuf (impl, x, y, width, height); + glGetIntegerv (GL_VIEWPORT, viewport); + stage_x = viewport[0]; + stage_y = viewport[1]; + stage_width = viewport[2] - x; + stage_height = viewport[3] - y; + rowstride = width * 4; - return NULL; + pixels = g_malloc (height * rowstride); + + /* check whether we need to read into a smaller temporary buffer */ + glReadPixels (x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + /* vertically flip the buffer in-place */ + for (y=0; y< height/2; y++) + { + guchar temprow[rowstride]; + if (y != height-y-1) /* skip center row */ + { + memcpy (temprow, + pixels + y * rowstride, rowstride); + memcpy (pixels + y * rowstride, + pixels + (height-y-1) * rowstride, rowstride); + memcpy (pixels + (height-y-1) * rowstride, + temprow, + rowstride); + } + } + + return pixels; } /** @@ -1161,7 +1195,8 @@ clutter_stage_set_title (ClutterStage *stage, priv->title = g_strdup (title); impl = CLUTTER_STAGE_WINDOW (priv->impl); - CLUTTER_STAGE_WINDOW_GET_IFACE (impl)->set_title (impl, priv->title); + if (CLUTTER_STAGE_WINDOW_GET_IFACE(impl)->set_title != NULL) + CLUTTER_STAGE_WINDOW_GET_IFACE (impl)->set_title (impl, priv->title); g_object_notify (G_OBJECT (stage), "title"); } diff --git a/clutter/clutter-stage.h b/clutter/clutter-stage.h index ff2954f86..36abf9eb7 100644 --- a/clutter/clutter-stage.h +++ b/clutter/clutter-stage.h @@ -30,7 +30,10 @@ #include #include #include + +#ifdef USE_GDKPIXBUF #include +#endif G_BEGIN_DECLS @@ -188,7 +191,7 @@ void clutter_stage_hide_cursor (ClutterStage *stage); ClutterActor *clutter_stage_get_actor_at_pos (ClutterStage *stage, gint x, gint y); -GdkPixbuf * clutter_stage_snapshot (ClutterStage *stage, +guchar *clutter_stage_read_pixels (ClutterStage *stage, gint x, gint y, gint width, diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index fd85a5588..ae3084b31 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -30,9 +30,9 @@ * #ClutterTexture is a base class for displaying and manipulating pixel * buffer type data. * - * The clutter_texture_set_from_rgb_data() and clutter_texture_set_pixbuf() - * functions are used to copy image data into texture memory and subsequently - * realize the texture. + * The clutter_texture_set_from_rgb_data() and + * clutter_texture_set_from_file() functions are used to copy image + * data into texture memory and subsequently realize the texture. * * If texture reads are supported by underlying GL implementation, * unrealizing/hiding frees image data from texture memory moving to main @@ -56,24 +56,19 @@ #include "clutter-feature.h" #include "clutter-util.h" #include "clutter-private.h" +#include "clutter-scriptable.h" #include "clutter-debug.h" #include "clutter-fixed.h" -#include "cogl.h" +#include "cogl/cogl.h" -G_DEFINE_TYPE (ClutterTexture, clutter_texture, CLUTTER_TYPE_ACTOR); +static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -#define PIXEL_TYPE CGL_UNSIGNED_BYTE -#else -#define PIXEL_TYPE CGL_UNSIGNED_INT_8_8_8_8_REV -#endif - -typedef struct { - gint pos; - gint size; - gint waste; -} ClutterTextureTileDimension; +G_DEFINE_TYPE_WITH_CODE (ClutterTexture, + clutter_texture, + CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, + clutter_scriptable_iface_init)); #define CLUTTER_TEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_TEXTURE, ClutterTexturePrivate)) @@ -81,38 +76,36 @@ struct _ClutterTexturePrivate { gint width; gint height; - COGLenum pixel_format; - COGLenum pixel_type; - COGLenum target_type; - GdkPixbuf *local_pixbuf; /* non video memory copy */ guint sync_actor_size : 1; gint max_tile_waste; guint filter_quality; - guint repeat_x : 1; /* non working */ - guint repeat_y : 1; /* non working */ - guint is_tiled : 1; - ClutterTextureTileDimension *x_tiles; - ClutterTextureTileDimension *y_tiles; - gint n_x_tiles; - gint n_y_tiles; - COGLuint *tiles; + guint repeat_x : 1; + guint repeat_y : 1; + CoglHandle texture; + gboolean no_slice; ClutterActor *fbo_source; - COGLuint fbo_handle; + CoglHandle fbo_handle; + + /* Non video memory copy of image data */ + guint local_data_width, local_data_height; + guint local_data_rowstride; + guint local_data_has_alpha; + guchar *local_data; }; enum { PROP_0, - PROP_PIXBUF, - PROP_USE_TILES, + PROP_NO_SLICE, PROP_MAX_TILE_WASTE, - PROP_PIXEL_TYPE, /* Texture type */ PROP_PIXEL_FORMAT, /* Texture format */ PROP_SYNC_SIZE, PROP_REPEAT_Y, PROP_REPEAT_X, - PROP_FILTER_QUALITY + PROP_FILTER_QUALITY, + PROP_COGL_TEXTURE, + PROP_FILENAME }; enum @@ -127,278 +120,18 @@ static int texture_signals[LAST_SIGNAL] = { 0 }; static void texture_fbo_free_resources (ClutterTexture *texture); +static void +clutter_texture_save_to_local_data (ClutterTexture *texture); + +static void +clutter_texture_load_from_local_data (ClutterTexture *texture); + GQuark clutter_texture_error_quark (void) { return g_quark_from_static_string ("clutter-texture-error-quark"); } -static guchar* -un_pre_multiply_alpha (const guchar *data, - gint width, - gint height, - gint rowstride) -{ - gint x,y; - unsigned char *ret, *dst, *src; - - ret = dst = g_malloc(sizeof(guchar) * height * rowstride); - - /* FIXME: Optimise */ - for (y = 0; y < height; y++) - { - src = (guchar*)data + y * rowstride; - for (x = 0; x < width; x++) - { - guchar alpha = src[3]; - if (alpha == 0) - { - src[0] = src[1] = src[2] = src[3] = alpha; - } - else - { - dst[0] = (((src[0] >> 16) & 0xff) * 255 ) / alpha; - dst[1] = (((src[1] >> 8) & 0xff) * 255 ) / alpha; - dst[2] = (((src[2] >> 0) & 0xff) * 255 ) / alpha; - dst[3] = alpha; - } - dst += 4; - src += 4; - } - } - - return ret; -} - -#ifndef HAVE_COGL_GL -static guchar * -rgb_to_bgr (const guchar *data, - gboolean has_alpha, - gint width, - gint height, - gint rowstride) -{ - gint x,y, bpp = 4; - unsigned char *ret, *dst, *src; - - ret = dst = g_malloc(sizeof(guchar) * height * rowstride); - - if (!has_alpha) - bpp = 3; - - /* FIXME: Optimise */ - for (y = 0; y < height; y++) - { - src = (guchar*)data + y * rowstride; - for (x = 0; x < width; x++) - { - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - if (has_alpha) - dst[3] = src[3]; - dst += bpp; - src += bpp; - } - } - - return ret; -} -#endif /* !HAVE_COGL_GL */ - -static int -tile_dimension (int to_fill, - int start_size, - int waste, - ClutterTextureTileDimension *tiles) -{ - int pos = 0; - int n_tiles = 0; - int size = start_size; - - while (TRUE) - { - if (tiles) - { - tiles[n_tiles].pos = pos; - tiles[n_tiles].size = size; - tiles[n_tiles].waste = 0; - } - - n_tiles++; - - if (to_fill <= size) - { - if (tiles) - tiles[n_tiles-1].waste = size - to_fill; - break; - } - else - { - to_fill -= size; pos += size; - while (size >= 2 * to_fill || size - to_fill > waste) - size /= 2; - } - } - - return n_tiles; -} - -static void -texture_init_tiles (ClutterTexture *texture) -{ - ClutterTexturePrivate *priv; - gint x_pot, y_pot; - - priv = texture->priv; - - x_pot = clutter_util_next_p2 (priv->width); - y_pot = clutter_util_next_p2 (priv->height); - - while (!(cogl_texture_can_size (CGL_TEXTURE_2D, - priv->pixel_format, - priv->pixel_type, - x_pot, y_pot) - && (x_pot - priv->width < priv->max_tile_waste) - && (y_pot - priv->height < priv->max_tile_waste))) - { - CLUTTER_NOTE (TEXTURE, "x_pot:%i - width:%i < max_waste:%i", - x_pot, - priv->width, - priv->max_tile_waste); - - CLUTTER_NOTE (TEXTURE, "y_pot:%i - height:%i < max_waste:%i", - y_pot, - priv->height, - priv->max_tile_waste); - - if (x_pot > y_pot) - x_pot /= 2; - else - y_pot /= 2; - - g_return_if_fail (x_pot != 0 || y_pot != 0); - } - - if (priv->x_tiles) - g_free (priv->x_tiles); - - priv->n_x_tiles = tile_dimension (priv->width, x_pot, - priv->max_tile_waste, NULL); - if (priv->y_tiles) - g_free (priv->y_tiles); - priv->n_y_tiles = tile_dimension (priv->height, y_pot, - priv->max_tile_waste, NULL); - - if (priv->n_x_tiles == 1 && priv->n_y_tiles == 1) - { - /* So were not actually tiled... */ - priv->n_x_tiles = priv->n_y_tiles = 0; - priv->is_tiled = FALSE; - return; - } - - priv->x_tiles = g_new (ClutterTextureTileDimension, priv->n_x_tiles); - tile_dimension (priv->width, x_pot, priv->max_tile_waste, priv->x_tiles); - - priv->y_tiles = g_new (ClutterTextureTileDimension, priv->n_y_tiles); - tile_dimension (priv->height, y_pot, priv->max_tile_waste, priv->y_tiles); - - CLUTTER_NOTE (TEXTURE, - "x_pot:%i, width:%i, y_pot:%i, height: %i " - "max_waste:%i, n_x_tiles: %i, n_y_tiles: %i", - x_pot, priv->width, y_pot, priv->height, - priv->max_tile_waste, - priv->n_x_tiles, priv->n_y_tiles); -} - -static void -texture_render_to_gl_quad (ClutterTexture *texture, - int x_1, - int y_1, - int x_2, - int y_2) -{ - int qx1 = 0, qx2 = 0, qy1 = 0, qy2 = 0; - int qwidth = 0, qheight = 0; - int x, y, i =0, lastx = 0, lasty = 0; - float tx, ty; - - ClutterTexturePrivate *priv; - - priv = texture->priv; - - qwidth = x_2 - x_1; - qheight = y_2 - y_1; - - if (!priv->is_tiled) - { - cogl_texture_bind (priv->target_type, priv->tiles[0]); - - if (priv->target_type == CGL_TEXTURE_2D) /* POT */ - { - tx = (float) priv->width / clutter_util_next_p2 (priv->width); - ty = (float) priv->height / clutter_util_next_p2 (priv->height); - } - else - { - tx = (float) priv->width; - ty = (float) priv->height; - - } - - qx1 = x_1; qx2 = x_2; - qy1 = y_1; qy2 = y_2; - - cogl_texture_quad (x_1, x_2, y_1, y_2, - 0, - 0, - CLUTTER_FLOAT_TO_FIXED (tx), - CLUTTER_FLOAT_TO_FIXED (ty)); - - return; - } - - for (x = 0; x < priv->n_x_tiles; x++) - { - lasty = 0; - - for (y=0; y < priv->n_y_tiles; y++) - { - int actual_w, actual_h; - - cogl_texture_bind (priv->target_type, priv->tiles[i]); - - actual_w = priv->x_tiles[x].size - priv->x_tiles[x].waste; - actual_h = priv->y_tiles[y].size - priv->y_tiles[y].waste; - - CLUTTER_NOTE (TEXTURE, - "rendering text tile x: %i, y: %i - %ix%i", - x, y, actual_w, actual_h); - - tx = (float) actual_w / priv->x_tiles[x].size; - ty = (float) actual_h / priv->y_tiles[y].size; - - qx1 = x_1 + lastx; - qx2 = qx1 + ((qwidth * actual_w ) / priv->width ); - - qy1 = y_1 + lasty; - qy2 = qy1 + ((qheight * actual_h) / priv->height ); - - cogl_texture_quad (qx1, qx2, qy1, qy2, - 0, - 0, - CLUTTER_FLOAT_TO_FIXED (tx), - CLUTTER_FLOAT_TO_FIXED (ty)); - - lasty += (qy2 - qy1) ; - - i++; - } - lastx += (qx2 - qx1); - } -} - static void texture_free_gl_resources (ClutterTexture *texture) { @@ -408,217 +141,11 @@ texture_free_gl_resources (ClutterTexture *texture) CLUTTER_MARK(); - if (priv->tiles) + if (priv->texture != COGL_INVALID_HANDLE) { - if (!priv->is_tiled) - cogl_textures_destroy (1, priv->tiles); - else - cogl_textures_destroy (priv->n_x_tiles * priv->n_y_tiles, priv->tiles); - - g_free (priv->tiles); - priv->tiles = NULL; + cogl_texture_unref (priv->texture); + priv->texture = COGL_INVALID_HANDLE; } - - if (priv->x_tiles) - { - g_free (priv->x_tiles); - priv->x_tiles = NULL; - } - - if (priv->y_tiles) - { - g_free (priv->y_tiles); - priv->y_tiles = NULL; - } -} - -static void inline -texture_upload_data (ClutterTexture *texture, - const guchar *data, - gboolean has_alpha, - gint width, - gint height, - gint rowstride, - gint bpp) -{ - ClutterTexturePrivate *priv; - gint x, y; - gint i = 0; - gboolean create_textures = FALSE; - GdkPixbuf *master_pixbuf = NULL; - - priv = texture->priv; - - g_return_if_fail (data != NULL); - - CLUTTER_MARK(); - - if (!priv->is_tiled) - { - /* Single Texture */ - if (!priv->tiles) - { - priv->tiles = g_new (COGLuint, 1); - glGenTextures (1, priv->tiles); - create_textures = TRUE; - } - - CLUTTER_NOTE (TEXTURE, "syncing for single tile"); - - cogl_texture_bind (priv->target_type, priv->tiles[0]); - cogl_texture_set_alignment (priv->target_type, 4, priv->width); - - cogl_texture_set_filters - (priv->target_type, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST); - - cogl_texture_set_wrap (priv->target_type, - priv->repeat_x ? CGL_REPEAT : CGL_CLAMP_TO_EDGE, - priv->repeat_y ? CGL_REPEAT : CGL_CLAMP_TO_EDGE); - - if (create_textures) - { - gint tex_width, tex_height; - - tex_width = priv->width; - tex_height = priv->height; - - if (priv->target_type == CGL_TEXTURE_2D) /* POT */ - { - tex_width = clutter_util_next_p2 (priv->width); - tex_height = clutter_util_next_p2 (priv->height); - } - - cogl_texture_image_2d (priv->target_type, - CGL_RGBA, - tex_width, - tex_height, - priv->pixel_format, - priv->pixel_type, - NULL); - } - - cogl_texture_sub_image_2d (priv->target_type, - 0, - 0, - width, - height, - priv->pixel_format, - priv->pixel_type, - data); - return; - } - - /* Multiple tiled texture */ - - CLUTTER_NOTE (TEXTURE, - "syncing for multiple tiles for %ix%i pixbuf", - priv->width, priv->height); - - g_return_if_fail (priv->x_tiles != NULL && priv->y_tiles != NULL); - - master_pixbuf = gdk_pixbuf_new_from_data (data, - GDK_COLORSPACE_RGB, - has_alpha, - 8, - width, height, rowstride, - NULL, NULL); - - if (priv->tiles == NULL) - { - priv->tiles = g_new (COGLuint, priv->n_x_tiles * priv->n_y_tiles); - glGenTextures (priv->n_x_tiles * priv->n_y_tiles, priv->tiles); - create_textures = TRUE; - } - - for (x = 0; x < priv->n_x_tiles; x++) - for (y = 0; y < priv->n_y_tiles; y++) - { - GdkPixbuf *pixtmp; - gint src_h, src_w; - - src_w = priv->x_tiles[x].size; - src_h = priv->y_tiles[y].size; - - pixtmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - has_alpha, - 8, - src_w, src_h); - - /* clip */ - if (priv->x_tiles[x].pos + src_w > priv->width) - src_w = priv->width - priv->x_tiles[x].pos; - - if (priv->y_tiles[y].pos + src_h > priv->height) - src_h = priv->height - priv->y_tiles[y].pos; - - gdk_pixbuf_copy_area (master_pixbuf, - priv->x_tiles[x].pos, - priv->y_tiles[y].pos, - src_w, - src_h, - pixtmp, - 0, 0); -#ifdef CLUTTER_DUMP_TILES - { - gchar *filename; - - filename = - g_strdup_printf("/tmp/%i-%i-%i.png", - clutter_actor_get_gid (CLUTTER_ACTOR (texture)), - x, y); - - printf ("saving %s\n", filename); - - gdk_pixbuf_save (pixtmp, filename , "png", NULL, NULL); - g_free (filename); - } -#endif - - cogl_texture_bind (priv->target_type, priv->tiles[i]); - - cogl_texture_set_alignment (priv->target_type, - 4, priv->x_tiles[x].size); - - cogl_texture_set_filters - (priv->target_type, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST); - - cogl_texture_set_wrap (priv->target_type, - priv->repeat_x ? CGL_REPEAT : CGL_CLAMP_TO_EDGE, - priv->repeat_y ? CGL_REPEAT : CGL_CLAMP_TO_EDGE); - if (create_textures) - { - cogl_texture_image_2d (priv->target_type, - CGL_RGBA, - gdk_pixbuf_get_width (pixtmp), - gdk_pixbuf_get_height (pixtmp), - priv->pixel_format, - priv->pixel_type, - gdk_pixbuf_get_pixels (pixtmp)); - } - else - { - /* Textures already created, so just update whats inside - */ - cogl_texture_sub_image_2d (priv->target_type, - 0, - 0, - gdk_pixbuf_get_width (pixtmp), - gdk_pixbuf_get_height (pixtmp), - priv->pixel_format, - priv->pixel_type, - gdk_pixbuf_get_pixels (pixtmp)); - } - - g_object_unref (pixtmp); - - i++; - } - - g_object_unref (master_pixbuf); } static void @@ -630,7 +157,7 @@ clutter_texture_unrealize (ClutterActor *actor) texture = CLUTTER_TEXTURE(actor); priv = texture->priv; - if (priv->tiles == NULL) + if (priv->texture == COGL_INVALID_HANDLE) return; /* there's no need to read the pixels back when unrealizing inside @@ -642,11 +169,11 @@ clutter_texture_unrealize (ClutterActor *actor) CLUTTER_MARK(); - if (priv->fbo_source) + if (priv->fbo_source != COGL_INVALID_HANDLE) { /* Free up our fbo handle and texture resources, realize will recreate */ - cogl_offscreen_destroy (priv->fbo_handle); - priv->fbo_handle = 0; + cogl_offscreen_unref (priv->fbo_handle); + priv->fbo_handle = COGL_INVALID_HANDLE; texture_free_gl_resources (texture); return; } @@ -660,10 +187,10 @@ clutter_texture_unrealize (ClutterActor *actor) * * Or make it controllable via a property. */ - if (priv->local_pixbuf == NULL) + if (priv->local_data == NULL) { - priv->local_pixbuf = clutter_texture_get_pixbuf (texture); - CLUTTER_NOTE (TEXTURE, "moved pixels into system (pixbuf) mem"); + clutter_texture_save_to_local_data (texture); + CLUTTER_NOTE (TEXTURE, "moved pixels into system mem"); } texture_free_gl_resources (texture); @@ -687,24 +214,24 @@ clutter_texture_realize (ClutterActor *actor) { /* Handle FBO's */ - priv->tiles = g_new (COGLuint, 1); + if (priv->texture != COGL_INVALID_HANDLE) + cogl_texture_unref (priv->texture); - /* FIXME: needs a cogl wrapper */ - glGenTextures (1, priv->tiles); + priv->texture = cogl_texture_new_with_size + (priv->width, + priv->height, + priv->no_slice ? -1 : priv->max_tile_waste, + COGL_PIXEL_FORMAT_RGBA_8888); - cogl_texture_bind (priv->target_type, priv->tiles[0]); + cogl_texture_set_filters (priv->texture, + priv->filter_quality + ? CGL_LINEAR : CGL_NEAREST, + priv->filter_quality + ? CGL_LINEAR : CGL_NEAREST); - cogl_texture_image_2d (priv->target_type, - CGL_RGBA, - priv->width, - priv->height, - priv->pixel_format, - priv->pixel_type, - NULL); + priv->fbo_handle = cogl_offscreen_new_to_texture (priv->texture); - priv->fbo_handle = cogl_offscreen_create (priv->tiles[0]); - - if (priv->fbo_handle == 0) + if (priv->fbo_handle == COGL_INVALID_HANDLE) { g_warning ("%s: Offscreen texture creation failed", G_STRLOC); CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); @@ -715,24 +242,20 @@ clutter_texture_realize (ClutterActor *actor) return; } - if (priv->local_pixbuf != NULL) + if (priv->local_data != NULL) { /* Move any local image data we have from unrealization * back into video memory. */ - if (priv->is_tiled) - texture_init_tiles (texture); - clutter_texture_set_pixbuf (texture, priv->local_pixbuf, NULL); - g_object_unref (priv->local_pixbuf); - priv->local_pixbuf = NULL; + clutter_texture_load_from_local_data (texture); } else { if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_READ_PIXELS)) { - /* Dont allow realization with no pixbuf - note set_pixbuf/data + /* Dont allow realization with no data - note set_data * will set realize flags. - */ + */ CLUTTER_NOTE (TEXTURE, "Texture has no image data cannot realize"); @@ -779,24 +302,13 @@ clutter_texture_paint (ClutterActor *self) ClutterTexturePrivate *priv = texture->priv; gint x_1, y_1, x_2, y_2; ClutterColor col = { 0xff, 0xff, 0xff, 0xff }; + ClutterColor transparent_col = { 0, 0, 0, 0 }; + ClutterFixed t_w, t_h; if (!CLUTTER_ACTOR_IS_REALIZED (CLUTTER_ACTOR(texture))) clutter_actor_realize (CLUTTER_ACTOR(texture)); - if (priv->tiles == NULL) - { - /* We just need do debug this state, it doesn't really need to - * throw a an error as what previously happened. Sub classes - * quite likely may not be able to realize. - */ - CLUTTER_NOTE (PAINT, "unable to paint texture '%s', contains no tiles", - clutter_actor_get_name (self) - ? clutter_actor_get_name (self) - : "unknown"); - return; - } - - if (priv->fbo_handle) + if (priv->fbo_handle != COGL_INVALID_HANDLE) { ClutterMainContext *context; ClutterShader *shader = NULL; @@ -812,21 +324,21 @@ clutter_texture_paint (ClutterActor *self) if (shader) clutter_shader_set_is_enabled (shader, FALSE); - cogl_offscreen_redirect_start (priv->fbo_handle, - priv->width, priv->height); + /* Redirect drawing to the fbo */ + cogl_draw_buffer (COGL_OFFSCREEN_BUFFER, priv->fbo_handle); + + /* cogl_paint_init is called to clear the buffers */ + cogl_paint_init (&transparent_col); /* Render out actor scene to fbo */ clutter_actor_paint (priv->fbo_source); - cogl_offscreen_redirect_end (priv->fbo_handle, - CLUTTER_STAGE_WIDTH(), - CLUTTER_STAGE_HEIGHT()); + /* Restore drawing to the frame buffer */ + cogl_draw_buffer (COGL_WINDOW_BUFFER, COGL_INVALID_HANDLE); /* If there is a shader on top of the shader stack, turn it back on. */ if (shader) clutter_shader_set_is_enabled (shader, TRUE); - - glBindTexture(CGL_TEXTURE_RECTANGLE_ARB, priv->tiles[0]); } CLUTTER_NOTE (PAINT, @@ -835,18 +347,6 @@ clutter_texture_paint (ClutterActor *self) : "unknown"); cogl_push_matrix (); - switch (priv->target_type) - { - case CGL_TEXTURE_2D: - cogl_enable (CGL_ENABLE_TEXTURE_2D|CGL_ENABLE_BLEND); - break; - case CGL_TEXTURE_RECTANGLE_ARB: - cogl_enable (CGL_ENABLE_TEXTURE_RECT|CGL_ENABLE_BLEND); - break; - default: - break; - } - col.alpha = clutter_actor_get_abs_opacity (self); cogl_color (&col); @@ -857,8 +357,22 @@ clutter_texture_paint (ClutterActor *self) x_1, y_1, x_2, y_2, clutter_actor_get_opacity (self)); - /* Paint will of translated us */ - texture_render_to_gl_quad (texture, 0, 0, x_2 - x_1, y_2 - y_1); + if (priv->repeat_x && priv->width > 0) + t_w = CFX_QDIV (CLUTTER_INT_TO_FIXED (x_2 - x_1), + CLUTTER_INT_TO_FIXED (priv->width)); + else + t_w = CFX_ONE; + if (priv->repeat_y && priv->height > 0) + t_h = CFX_QDIV (CLUTTER_INT_TO_FIXED (y_2 - y_1), + CLUTTER_INT_TO_FIXED (priv->height)); + else + t_h = CFX_ONE; + + /* Paint will have translated us */ + cogl_texture_rectangle (priv->texture, 0, 0, + CLUTTER_INT_TO_FIXED (x_2 - x_1), + CLUTTER_INT_TO_FIXED (y_2 - y_1), + 0, 0, t_w, t_h); cogl_pop_matrix (); } @@ -876,7 +390,8 @@ clutter_texture_request_coords (ClutterActor *self, ((box->y2 - box->y1) != (old_request.y2 - old_request.y1))) texture->priv->sync_actor_size = FALSE; - CLUTTER_ACTOR_CLASS (clutter_texture_parent_class)->request_coords (self, box); + CLUTTER_ACTOR_CLASS (clutter_texture_parent_class) + ->request_coords (self, box); } static void @@ -890,10 +405,10 @@ clutter_texture_dispose (GObject *object) texture_free_gl_resources (texture); texture_fbo_free_resources (texture); - if (priv->local_pixbuf != NULL) + if (priv->local_data != NULL) { - g_object_unref (priv->local_pixbuf); - priv->local_pixbuf = NULL; + g_free (priv->local_data); + priv->local_data = NULL; } G_OBJECT_CLASS (clutter_texture_parent_class)->dispose (object); @@ -913,35 +428,44 @@ clutter_texture_set_property (GObject *object, switch (prop_id) { - case PROP_PIXBUF: - if (g_value_get_object (value)) - clutter_texture_set_pixbuf (texture, - GDK_PIXBUF (g_value_get_object (value)), - NULL); - break; - case PROP_USE_TILES: - priv->is_tiled = g_value_get_boolean (value); - - if (priv->target_type == CGL_TEXTURE_RECTANGLE_ARB && priv->is_tiled) - priv->target_type = CGL_TEXTURE_2D; - - CLUTTER_NOTE (TEXTURE, "Texture is tiled ? %s", - priv->is_tiled ? "yes" : "no"); - break; case PROP_MAX_TILE_WASTE: - priv->max_tile_waste = g_value_get_int (value); + clutter_texture_set_max_tile_waste (texture, + g_value_get_int (value)); break; case PROP_SYNC_SIZE: priv->sync_actor_size = g_value_get_boolean (value); break; case PROP_REPEAT_X: - priv->repeat_x = g_value_get_boolean (value); + if (priv->repeat_x != g_value_get_boolean (value)) + { + priv->repeat_x = !priv->repeat_x; + if (CLUTTER_ACTOR_IS_VISIBLE (texture)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (texture)); + } break; case PROP_REPEAT_Y: - priv->repeat_y = g_value_get_boolean (value); + if (priv->repeat_y != g_value_get_boolean (value)) + { + priv->repeat_y = !priv->repeat_y; + if (CLUTTER_ACTOR_IS_VISIBLE (texture)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (texture)); + } break; case PROP_FILTER_QUALITY: - priv->filter_quality = g_value_get_int (value); + clutter_texture_set_filter_quality (texture, + g_value_get_int (value)); + break; + case PROP_COGL_TEXTURE: + clutter_texture_set_cogl_texture + (texture, (CoglHandle) g_value_get_boxed (value)); + break; + case PROP_FILENAME: + clutter_texture_set_from_file (texture, + g_value_get_string (value), + NULL); + break; + case PROP_NO_SLICE: + priv->no_slice = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -963,24 +487,14 @@ clutter_texture_get_property (GObject *object, switch (prop_id) { - case PROP_PIXBUF: - { - GdkPixbuf *pixb; - pixb = clutter_texture_get_pixbuf (texture); - g_value_take_object (value, pixb); - } - break; - case PROP_USE_TILES: - g_value_set_boolean (value, priv->is_tiled); - break; case PROP_MAX_TILE_WASTE: - g_value_set_int (value, priv->max_tile_waste); - break; - case PROP_PIXEL_TYPE: - g_value_set_int (value, priv->pixel_type); + g_value_set_int (value, clutter_texture_get_max_tile_waste (texture)); break; case PROP_PIXEL_FORMAT: - g_value_set_int (value, priv->pixel_format); + if (priv->texture == COGL_INVALID_HANDLE) + g_value_set_int (value, COGL_PIXEL_FORMAT_ANY); + else + g_value_set_int (value, cogl_texture_get_format (priv->texture)); break; case PROP_SYNC_SIZE: g_value_set_boolean (value, priv->sync_actor_size); @@ -992,7 +506,13 @@ clutter_texture_get_property (GObject *object, g_value_set_boolean (value, priv->repeat_y); break; case PROP_FILTER_QUALITY: - g_value_set_int (value, priv->filter_quality); + g_value_set_int (value, clutter_texture_get_filter_quality (texture)); + break; + case PROP_COGL_TEXTURE: + g_value_set_boxed (value, clutter_texture_get_cogl_texture (texture)); + break; + case PROP_NO_SLICE: + g_value_set_boolean (value, priv->no_slice); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -1000,7 +520,6 @@ clutter_texture_get_property (GObject *object, } } - static void clutter_texture_class_init (ClutterTextureClass *klass) { @@ -1023,28 +542,6 @@ clutter_texture_class_init (ClutterTextureClass *klass) gobject_class->set_property = clutter_texture_set_property; gobject_class->get_property = clutter_texture_get_property; - g_object_class_install_property - (gobject_class, PROP_PIXBUF, - g_param_spec_object ("pixbuf", - "Pixbuf source for Texture.", - "Pixbuf source for Texture.", - GDK_TYPE_PIXBUF, - CLUTTER_PARAM_READWRITE)); - - g_object_class_install_property - (gobject_class, PROP_USE_TILES, - g_param_spec_boolean ("tiled", - "Enable use of tiled textures", - "Enables the use of tiled GL textures to more " - "efficiently use available texture memory", - /* FIXME: This default set at runtime :/ - * As tiling depends on what GL features available. - * Need to figure out better solution - */ - (clutter_feature_available - (CLUTTER_FEATURE_TEXTURE_RECTANGLE) == FALSE), - G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, PROP_SYNC_SIZE, g_param_spec_boolean ("sync-size", @@ -1054,12 +551,23 @@ clutter_texture_class_init (ClutterTextureClass *klass) TRUE, CLUTTER_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, PROP_NO_SLICE, + g_param_spec_boolean ("disable-slicing", + "Disable Slicing", + "Force the underlying texture to be singlular" + "and not made of of smaller space saving " + "inidivual textures.", + FALSE, + G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_REPEAT_X, g_param_spec_boolean ("repeat-x", "Tile underlying pixbuf in x direction", - "Reapeat underlying pixbuf rather than scale " - "in x direction. Currently ignored", + "Repeat underlying pixbuf rather than scale " + "in x direction.", FALSE, CLUTTER_PARAM_READWRITE)); @@ -1067,8 +575,8 @@ clutter_texture_class_init (ClutterTextureClass *klass) (gobject_class, PROP_REPEAT_Y, g_param_spec_boolean ("repeat-y", "Tile underlying pixbuf in y direction", - "Reapeat underlying pixbuf rather than scale " - "in y direction. Currently ignored", + "Repeat underlying pixbuf rather than scale " + "in y direction.", FALSE, CLUTTER_PARAM_READWRITE)); @@ -1093,33 +601,41 @@ clutter_texture_class_init (ClutterTextureClass *klass) g_param_spec_int ("tile-waste", "Tile dimension to waste", "Max wastage dimension of a texture when using " - "tiled textures. Bigger values use less textures, " + "sliced textures or -1 to disable slicing. " + "Bigger values use less textures, " "smaller values less texture memory.", - 0, + -1, G_MAXINT, 64, G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE)); - g_object_class_install_property - (gobject_class, PROP_PIXEL_TYPE, - g_param_spec_int ("pixel-type", - "Texture Pixel Type", - "GL texture pixel type used", - 0, - G_MAXINT, - PIXEL_TYPE, - G_PARAM_READABLE)); - g_object_class_install_property (gobject_class, PROP_PIXEL_FORMAT, g_param_spec_int ("pixel-format", "Texture pixel format", - "GL texture pixel format used", + "CoglPixelFormat to use.", 0, G_MAXINT, - CGL_RGBA, + COGL_PIXEL_FORMAT_RGBA_8888, G_PARAM_READABLE)); + g_object_class_install_property + (gobject_class, PROP_COGL_TEXTURE, + g_param_spec_boxed ("cogl-texture", + "COGL Texture", + "The underlying COGL texture handle used to draw " + "this actor", + CLUTTER_TYPE_TEXTURE_HANDLE, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, PROP_FILENAME, + g_param_spec_string ("filename", + "Filename", + "The full path of the file containing the texture", + NULL, + G_PARAM_WRITABLE)); + /** * ClutterTexture::size-change: * @texture: the texture which received the signal @@ -1157,6 +673,79 @@ clutter_texture_class_init (ClutterTextureClass *klass) 0); } +static ClutterScriptableIface *parent_scriptable_iface = NULL; + +static void +clutter_texture_set_custom_property (ClutterScriptable *scriptable, + ClutterScript *script, + const gchar *name, + const GValue *value) +{ + ClutterTexture *texture = CLUTTER_TEXTURE (scriptable); + + if (strcmp ("filename", name) == 0) + { + const gchar *str = g_value_get_string (value); + gchar *path; + GError *error; + + if (g_path_is_absolute (str)) + path = g_strdup (str); + else + { + gchar *dirname = NULL; + gboolean is_filename = FALSE; + + g_object_get (script, "filename-set", &is_filename, NULL); + if (is_filename) + { + gchar *filename = NULL; + + g_object_get (script, "filename", &filename, NULL); + dirname = g_path_get_dirname (filename); + + g_free (filename); + } + else + dirname = g_get_current_dir (); + + path = g_build_filename (dirname, str, NULL); + g_free (dirname); + } + + error = NULL; + clutter_texture_set_from_file (texture, path, &error); + if (error) + { + g_warning ("Unable to open image path at `%s': %s", + path, + error->message); + g_error_free (error); + } + + g_free (path); + } + else + { + /* chain up */ + if (parent_scriptable_iface->set_custom_property) + parent_scriptable_iface->set_custom_property (scriptable, script, + name, + value); + } +} + +static void +clutter_scriptable_iface_init (ClutterScriptableIface *iface) +{ + parent_scriptable_iface = g_type_interface_peek_parent (iface); + + if (!parent_scriptable_iface) + parent_scriptable_iface = g_type_default_interface_peek (CLUTTER_TYPE_SCRIPTABLE); + + iface->set_custom_property = clutter_texture_set_custom_property; +} + static void clutter_texture_init (ClutterTexture *self) { @@ -1166,280 +755,218 @@ clutter_texture_init (ClutterTexture *self) priv->max_tile_waste = 64; priv->filter_quality = 1; - priv->is_tiled = TRUE; - priv->pixel_type = PIXEL_TYPE; - priv->pixel_format = CGL_RGBA; priv->repeat_x = FALSE; priv->repeat_y = FALSE; priv->sync_actor_size = TRUE; - - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE)) - { - priv->target_type = CGL_TEXTURE_RECTANGLE_ARB; - priv->is_tiled = FALSE; - } - else - priv->target_type = CGL_TEXTURE_2D; + priv->texture = COGL_INVALID_HANDLE; + priv->fbo_handle = COGL_INVALID_HANDLE; + priv->local_data = NULL; } static void -pixbuf_destroy_notify (guchar *pixels, gpointer data) +clutter_texture_save_to_local_data (ClutterTexture *texture) { - g_free (pixels); -} - -static GdkPixbuf * -texture_get_tile_pixbuf (ClutterTexture *texture, - COGLuint texture_id, - gint bpp, - gint ix, - gint iy) -{ -#ifdef HAVE_COGL_GL ClutterTexturePrivate *priv; - guchar *pixels = NULL; - guint tex_width, tex_height; + int bpp; + CoglPixelFormat pixel_format; priv = texture->priv; - cogl_texture_bind (priv->target_type, texture_id); - if (!priv->is_tiled) + if (priv->local_data) { - tex_width = priv->width; - tex_height = priv->height; - } - else - { - tex_width = priv->x_tiles[ix].size; - tex_height = priv->y_tiles[iy].size; + g_free (priv->local_data); + priv->local_data = NULL; } - /* Make sure if we aren't using rectangular textures that we increase the - * texture size accordingly. - */ - if (priv->target_type == CGL_TEXTURE_2D) /* POT */ + if (priv->texture == COGL_INVALID_HANDLE) + return; + + priv->local_data_width = cogl_texture_get_width (priv->texture); + priv->local_data_height = cogl_texture_get_height (priv->texture); + pixel_format = cogl_texture_get_format (priv->texture); + priv->local_data_has_alpha = pixel_format & COGL_A_BIT; + bpp = priv->local_data_has_alpha ? 4 : 3; + /* Align to 4 bytes */ + priv->local_data_rowstride = (priv->local_data_width * bpp + 3) & ~3; + + /* Store the filter quality and max_tile_waste from the texture + properties so that they will be restored the data is loaded + again */ + priv->max_tile_waste = clutter_texture_get_max_tile_waste (texture); + priv->filter_quality = clutter_texture_get_filter_quality (texture); + + priv->local_data = g_malloc (priv->local_data_rowstride + * priv->local_data_height); + + if (cogl_texture_get_data (priv->texture, + priv->local_data_has_alpha + ? COGL_PIXEL_FORMAT_RGBA_8888 + : COGL_PIXEL_FORMAT_RGB_888, + priv->local_data_rowstride, + priv->local_data) == 0) { - tex_width = clutter_util_next_p2 (tex_width); - tex_height = clutter_util_next_p2 (tex_height); + g_free (priv->local_data); + priv->local_data = NULL; } +} - cogl_texture_set_alignment (priv->target_type, 4, tex_width); +static void +clutter_texture_load_from_local_data (ClutterTexture *texture) +{ + ClutterTexturePrivate *priv; - if ((pixels = g_malloc (((tex_width * bpp + 3) &~ 3) * tex_height)) == NULL) - return NULL; + priv = texture->priv; - /* Read data from GL texture and return as pixbuf */ - /* No such func in GLES... */ - glGetTexImage (priv->target_type, - 0, - (priv->pixel_format == CGL_RGBA - || priv->pixel_format == CGL_BGRA) ? - CGL_RGBA : CGL_RGB, - PIXEL_TYPE, - (GLvoid *)pixels); + if (priv->local_data == NULL) + return; - - return gdk_pixbuf_new_from_data ((const guchar *)pixels, - GDK_COLORSPACE_RGB, - (priv->pixel_format == CGL_RGBA - || priv->pixel_format == CGL_BGRA), - 8, - tex_width, - tex_height, - ((tex_width * bpp + 3) &~ 3), - pixbuf_destroy_notify, - NULL); -#else - return NULL; -#endif + clutter_texture_set_from_rgb_data (texture, + priv->local_data, + priv->local_data_has_alpha, + priv->local_data_width, + priv->local_data_height, + priv->local_data_rowstride, + priv->local_data_has_alpha ? 4: 3, + 0, NULL); + + g_free (priv->local_data); + priv->local_data = NULL; } /** - * clutter_texture_get_pixbuf: + * clutter_texture_get_cogl_texture * @texture: A #ClutterTexture * - * Gets a #GdkPixbuf representation of the #ClutterTexture data. - * The created #GdkPixbuf is not owned by the texture but the caller. + * Returns a handle to the underlying COGL texture used for drawing + * the actor. No extra reference is taken so if you need to keep the + * handle then you should call cogl_texture_ref on it. * - * Note: %NULL is always returned with OpenGL ES. + * Since: 0.8 * - * Return value: A #GdkPixbuf, or %NULL on fail. + * Return value: COGL texture handle **/ -GdkPixbuf * -clutter_texture_get_pixbuf (ClutterTexture *texture) +CoglHandle +clutter_texture_get_cogl_texture (ClutterTexture *texture) { -#if HAVE_COGL_GL - ClutterTexturePrivate *priv; - GdkPixbuf *pixbuf; - GdkPixbuf *pixtmp; - int bpp = 4; + g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), COGL_INVALID_HANDLE); - priv = texture->priv; - - if (priv->tiles == NULL) - return NULL; - - if (priv->pixel_format == CGL_YCBCR_MESA) - return NULL; /* FIXME: convert YUV */ - - if (priv->pixel_format == CGL_RGB || priv->pixel_format == CGL_BGR) - bpp = 3; - - pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - priv->pixel_format == CGL_RGBA || - priv->pixel_format == CGL_BGRA, - 8, priv->width, priv->height); - - if (pixbuf == NULL) - return NULL; - - if (!priv->is_tiled) - { - pixtmp = texture_get_tile_pixbuf (texture, priv->tiles[0], bpp, 0, 0); - - if (pixtmp == NULL) - { - g_object_unref (pixbuf); - return NULL; - } - - /* Avoid a copy if the texture has the same size as the pixbuf */ - if ((gdk_pixbuf_get_width (pixtmp) == priv->width && - gdk_pixbuf_get_height (pixtmp) == priv->height)) - { - g_object_unref (pixbuf); - pixbuf = pixtmp; - } - else - { - gdk_pixbuf_copy_area (pixtmp, - 0, 0, - priv->width, priv->height, - pixbuf, - 0, 0); - g_object_unref (pixtmp); - } - } - else - { - int x,y,i; - - i = 0; - - for (x = 0; x < priv->n_x_tiles; x++) - for (y = 0; y < priv->n_y_tiles; y++) - { - gint src_w, src_h; - - pixtmp = texture_get_tile_pixbuf (texture, priv->tiles[i], bpp, x, y); - - if (pixtmp == NULL) - { - g_object_unref (pixbuf); - return NULL; - } - - src_w = priv->x_tiles[x].size; - src_h = priv->y_tiles[y].size; - - /* Clip */ - if (priv->x_tiles[x].pos + src_w > priv->width) - src_w = priv->width - priv->x_tiles[x].pos; - - if (priv->y_tiles[y].pos + src_h > priv->height) - src_h = priv->height - priv->y_tiles[y].pos; - - gdk_pixbuf_copy_area (pixtmp, - 0, 0, src_w, src_h, - pixbuf, - priv->x_tiles[x].pos, priv->y_tiles[y].pos); - - g_object_unref (pixtmp); - - i++; - } - - } - - return pixbuf; -#else - - /* FIXME: func call wont work for GLES... - * features need to reflect this. - */ - return NULL; -#endif + return texture->priv->texture; } -/* internal helper function for initialization of - * clutter_texture_set_from_rgb_data and clutter_texture_set_area_from_rgb_data. +/** + * clutter_texture_set_cogl_texture + * @texture: A #ClutterTexture + * @cogl_tex: A CoglHandle for a texture + * + * Replaces the underlying COGL texture drawn by this actor with + * @cogl_tex. A reference to the texture is taken so if the handle is + * no longer needed it should be deref'd with cogl_texture_unref. + * + * Since: 0.8 */ -static inline gboolean -texture_prepare_upload (gboolean initialize, - ClutterTexture *texture, - const guchar *data, - gboolean has_alpha, - gint width, - gint height, - gint rowstride, - gint bpp, - ClutterTextureFlags flags, - guchar **copy_data, - gboolean *texture_dirty, - gboolean *size_change) +void +clutter_texture_set_cogl_texture (ClutterTexture *texture, + CoglHandle cogl_tex) { - ClutterTexturePrivate *priv; - COGLenum prev_format; + ClutterTexturePrivate *priv; + gboolean size_change; + guint width, height; + + g_return_if_fail (CLUTTER_IS_TEXTURE (texture)); + g_return_if_fail (cogl_is_texture (cogl_tex)); + priv = texture->priv; - g_return_val_if_fail (data != NULL, FALSE); - /* Needed for GL_RGBA (internal format) and gdk pixbuf usage */ - g_return_val_if_fail (bpp == 4, FALSE); + width = cogl_texture_get_width (cogl_tex); + height = cogl_texture_get_height (cogl_tex); - if (initialize) + /* Reference the new texture now in case it is the same one we are + already using */ + cogl_texture_ref (cogl_tex); + + /* Remove FBO if exisiting */ + if (priv->fbo_source) + texture_fbo_free_resources (texture); + /* Remove old texture */ + texture_free_gl_resources (texture); + /* Use the new texture */ + priv->texture = cogl_tex; + + size_change = width != priv->width || height != priv->height; + priv->width = width; + priv->height = height; + + CLUTTER_NOTE (TEXTURE, "set size %ix%i\n", + priv->width, + priv->height); + + CLUTTER_ACTOR_SET_FLAGS (CLUTTER_ACTOR (texture), CLUTTER_ACTOR_REALIZED); + + if (size_change) { - *texture_dirty = *size_change = - (width != priv->width || height != priv->height) ; + g_signal_emit (texture, texture_signals[SIZE_CHANGE], + 0, priv->width, priv->height); + + if (priv->sync_actor_size) + { + clutter_actor_set_size (CLUTTER_ACTOR(texture), + priv->width, + priv->height); + /* The above call will clear sync_actor_size because the + size has changed so we need to put it back */ + priv->sync_actor_size = TRUE; + } } - prev_format = priv->pixel_format; + /* rename signal */ + g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0); - if (has_alpha) - priv->pixel_format = CGL_RGBA; - else - priv->pixel_format = CGL_RGB; + g_object_notify (G_OBJECT (texture), "cogl-texture"); - if (flags & CLUTTER_TEXTURE_RGB_FLAG_BGR) + /* If resized actor may need resizing but paint() will do this */ + if (CLUTTER_ACTOR_IS_VISIBLE (texture)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (texture)); +} + +static gboolean +clutter_texture_set_from_data (ClutterTexture *texture, + const guchar *data, + CoglPixelFormat source_format, + gint width, + gint height, + gint rowstride, + gint bpp, + GError **error) +{ + CoglHandle new_texture; + ClutterTexturePrivate *priv; + + priv = texture->priv; + + if ((new_texture = cogl_texture_new_from_data + (width, height, + priv->no_slice ? -1 : priv->max_tile_waste, + source_format, + COGL_PIXEL_FORMAT_ANY, + rowstride, + data)) == COGL_INVALID_HANDLE) { -#if HAVE_COGL_GL - if (has_alpha) - priv->pixel_format = CGL_BGRA; - else - priv->pixel_format = CGL_BGR; -#else - /* GLES has no BGR format*/ - *copy_data = rgb_to_bgr (data, has_alpha, width, height, rowstride); -#endif /* HAVE_COGL_GL */ + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "Failed to create COGL texture"); + + return FALSE; } - if (flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT) - *copy_data = un_pre_multiply_alpha (data, width, height, rowstride); + cogl_texture_set_filters (new_texture, + priv->filter_quality + ? CGL_LINEAR : CGL_NEAREST, + priv->filter_quality + ? CGL_LINEAR : CGL_NEAREST); - if (initialize) - { - if (prev_format != priv->pixel_format || priv->pixel_type != PIXEL_TYPE) - *texture_dirty = TRUE; - } - else - { - if (prev_format != priv->pixel_format || priv->pixel_type != PIXEL_TYPE) - { - g_warning ("%s: pixel format or type mismatch", G_STRFUNC); - return FALSE; - } - } + clutter_texture_set_cogl_texture (texture, new_texture); - priv->pixel_type = PIXEL_TYPE; + cogl_texture_unref (new_texture); return TRUE; } @@ -1476,101 +1003,45 @@ clutter_texture_set_from_rgb_data (ClutterTexture *texture, GError **error) { ClutterTexturePrivate *priv; - guchar *copy_data = NULL; - gboolean texture_dirty = TRUE, size_change = FALSE; + CoglPixelFormat source_format; + + g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE); priv = texture->priv; - /* Remove FBO if exisiting */ - if (priv->fbo_source) - texture_fbo_free_resources (texture); - - if (!texture_prepare_upload (TRUE, texture, data, has_alpha, - width, height, rowstride, bpp, flags, - ©_data, &texture_dirty, &size_change)) + /* Convert the flags to a CoglPixelFormat */ + if (has_alpha) { - return FALSE; - } - - priv->width = width; - priv->height = height; - - if (texture_dirty) - { - texture_free_gl_resources (texture); - - if (priv->is_tiled == FALSE) + if (bpp != 4) { - if (priv->target_type == CGL_TEXTURE_RECTANGLE_ARB && - !cogl_texture_can_size (CGL_TEXTURE_RECTANGLE_ARB, - priv->pixel_format, - priv->pixel_type, - priv->width, - priv->height)) - { - /* If we cant create NPOT tex of this size fall back to tiles */ - CLUTTER_NOTE (TEXTURE, - "Cannot make npots of size %ix%i " - "falling back to tiled", - priv->width, - priv->height); - - priv->target_type = CGL_TEXTURE_2D; - } - - if (priv->target_type == CGL_TEXTURE_2D && - !cogl_texture_can_size (CGL_TEXTURE_2D, - priv->pixel_format, - priv->pixel_type, - clutter_util_next_p2 (priv->width), - clutter_util_next_p2 (priv->height))) - { - priv->is_tiled = TRUE; - } + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "Unsupported BPP"); + return FALSE; } - - /* Figure our tiling etc */ - if (priv->is_tiled) - texture_init_tiles (texture); + source_format = COGL_PIXEL_FORMAT_RGBA_8888; } - - CLUTTER_NOTE (TEXTURE, "set size %ix%i\n", - priv->width, - priv->height); - - /* Set Error from this */ - texture_upload_data (texture, - copy_data != NULL ? copy_data : data, - has_alpha, - width, - height, - rowstride, - bpp); - - CLUTTER_ACTOR_SET_FLAGS (CLUTTER_ACTOR (texture), CLUTTER_ACTOR_REALIZED); - - if (size_change) + else { - g_signal_emit (texture, texture_signals[SIZE_CHANGE], - 0, priv->width, priv->height); - - if (priv->sync_actor_size) - clutter_actor_set_size (CLUTTER_ACTOR(texture), - priv->width, - priv->height); + if (bpp != 3) + { + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "Unsupported BPP"); + return FALSE; + } + source_format = COGL_PIXEL_FORMAT_RGB_888; } + if ((flags & CLUTTER_TEXTURE_RGB_FLAG_BGR)) + source_format |= COGL_BGR_BIT; + if ((flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT)) + source_format |= COGL_PREMULT_BIT; - /* rename signal */ - g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0); - - /* If resized actor may need resizing but paint() will do this */ - if (CLUTTER_ACTOR_IS_VISIBLE (texture)) - clutter_actor_queue_redraw (CLUTTER_ACTOR (texture)); - - if (copy_data != NULL) - g_free (copy_data); - - return TRUE; + return clutter_texture_set_from_data (texture, data, + source_format, + width, height, + rowstride, bpp, + error); } /** @@ -1600,7 +1071,6 @@ clutter_texture_set_from_yuv_data (ClutterTexture *texture, GError **error) { ClutterTexturePrivate *priv; - gboolean texture_dirty = TRUE, size_change = FALSE; g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE); @@ -1614,148 +1084,233 @@ clutter_texture_set_from_yuv_data (ClutterTexture *texture, priv = texture->priv; - if (priv->fbo_source) - texture_fbo_free_resources (texture); - - /* FIXME: check other image props */ - size_change = (width != priv->width || height != priv->height); - texture_dirty = size_change || (priv->pixel_format != CGL_YCBCR_MESA); - - priv->width = width; - priv->height = height; - priv->pixel_type = (flags & CLUTTER_TEXTURE_YUV_FLAG_YUV2) ? - CGL_UNSIGNED_SHORT_8_8_REV_MESA : - CGL_UNSIGNED_SHORT_8_8_MESA; - priv->pixel_format = CGL_YCBCR_MESA; - priv->target_type = CGL_TEXTURE_2D; - - if (texture_dirty) - texture_free_gl_resources (texture); - - if (!priv->tiles) + /* Convert the flags to a CoglPixelFormat */ + if ((flags & CLUTTER_TEXTURE_YUV_FLAG_YUV2)) { - priv->tiles = g_new (COGLuint, 1); - glGenTextures (1, priv->tiles); + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "YUV2 not supported"); + return FALSE; } - cogl_texture_bind (priv->target_type, priv->tiles[0]); + return clutter_texture_set_from_data (texture, data, + COGL_PIXEL_FORMAT_YUV, + width, height, + width * 3, 3, + error); +} - cogl_texture_set_filters (priv->target_type, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST); +/** + * clutter_texture_set_from_file: + * @texture: A #ClutterTexture + * @filename: The filename of the image in GLib file name encoding + * @error: Return location for a #GError, or %NULL + * + * Sets the #ClutterTexture image data from an image file. In case of + * failure, %FALSE is returned and @error is set. + * + * Return value: %TRUE if the image was successfully loaded and set + * + * Since: 0.8 + */ +gboolean +clutter_texture_set_from_file (ClutterTexture *texture, + const gchar *filename, + GError **error) +{ + CoglHandle new_texture; + ClutterTexturePrivate *priv; - if (texture_dirty) + priv = texture->priv; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if ((new_texture = cogl_texture_new_from_file + (filename, + priv->no_slice ? -1 : priv->max_tile_waste, + COGL_PIXEL_FORMAT_ANY, + error)) + == COGL_INVALID_HANDLE) { - gint new_width, new_height; - - new_width = clutter_util_next_p2 (priv->width); - new_height = clutter_util_next_p2 (priv->height); - - /* FIXME: need to check size limits correctly - does not - * seem to work if correct format and type are used so - * this is really a guess... - */ - if (cogl_texture_can_size (CGL_TEXTURE_2D, - CGL_RGBA, - CGL_UNSIGNED_BYTE, - new_width, new_height)) + /* If COGL didn't give an error then make one up */ + if (error && *error == NULL) { - cogl_texture_image_2d (priv->target_type, - priv->pixel_format, - new_width, new_height, - priv->pixel_format, - priv->pixel_type, - NULL); + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "Failed to create COGL texture"); } - else - { - g_set_error (error, CLUTTER_TEXTURE_ERROR, - CLUTTER_TEXTURE_ERROR_OUT_OF_MEMORY, - "Unable to allocate a texture of %d by %d pixels", - new_width, - new_height); - return FALSE; /* FIXME: add tiling */ - } + return FALSE; } - cogl_texture_sub_image_2d (priv->target_type, - 0, - 0, - priv->width, - priv->height, - priv->pixel_format, - priv->pixel_type, - data); + cogl_texture_set_filters (new_texture, + priv->filter_quality + ? CGL_LINEAR : CGL_NEAREST, + priv->filter_quality + ? CGL_LINEAR : CGL_NEAREST); - CLUTTER_ACTOR_SET_FLAGS (CLUTTER_ACTOR (texture), CLUTTER_ACTOR_REALIZED); + clutter_texture_set_cogl_texture (texture, new_texture); - if (size_change) - { - g_signal_emit (texture, texture_signals[SIZE_CHANGE], - 0, priv->width, priv->height); - - if (priv->sync_actor_size) - clutter_actor_set_size (CLUTTER_ACTOR (texture), - priv->width, - priv->height); - } - - g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0); - - if (CLUTTER_ACTOR_IS_VISIBLE (texture)) - clutter_actor_queue_redraw (CLUTTER_ACTOR(texture)); + cogl_texture_unref (new_texture); return TRUE; } /** - * clutter_texture_set_pixbuf: + * clutter_texture_set_filter_quality * @texture: A #ClutterTexture - * @pixbuf: A #GdkPixbuf - * @error: Return location for a #GError, or %NULL + * @filter_quality: A filter quality value * - * Sets a #ClutterTexture image data from a #GdkPixbuf. In case of - * failure, %FALSE is returned and @error is set. + * Sets the filter quality when scaling a texture. Only values 0 and 1 + * are currently supported, with 0 being lower quality but fast, 1 + * being better quality but slower. ( Currently just maps to + * GL_NEAREST / GL_LINEAR ). The default is 1. * - * Return value: %TRUE if the pixbuf was successfully set - * - * Since: 0.4 + * Since: 0.8 */ -gboolean -clutter_texture_set_pixbuf (ClutterTexture *texture, - GdkPixbuf *pixbuf, - GError **error) +void +clutter_texture_set_filter_quality (ClutterTexture *texture, + guint filter_quality) { ClutterTexturePrivate *priv; - g_return_val_if_fail (pixbuf != NULL, FALSE); + g_return_if_fail (CLUTTER_IS_TEXTURE (texture)); priv = texture->priv; - return clutter_texture_set_from_rgb_data (texture, - gdk_pixbuf_get_pixels (pixbuf), - gdk_pixbuf_get_has_alpha (pixbuf), - gdk_pixbuf_get_width (pixbuf), - gdk_pixbuf_get_height (pixbuf), - gdk_pixbuf_get_rowstride (pixbuf), - 4, - 0, - error); + if (filter_quality != clutter_texture_get_filter_quality (texture)) + { + priv->filter_quality = filter_quality; + + if (priv->texture != COGL_INVALID_HANDLE) + cogl_texture_set_filters (priv->texture, + filter_quality ? CGL_LINEAR : CGL_NEAREST, + filter_quality ? CGL_LINEAR : CGL_NEAREST); + + if (CLUTTER_ACTOR_IS_VISIBLE (texture)) + clutter_actor_queue_redraw (CLUTTER_ACTOR (texture)); + } } /** - * clutter_texture_new_from_pixbuf: - * @pixbuf: A #GdkPixbuf + * clutter_texture_get_filter_quality + * @texture: A #ClutterTexture * - * Creates a new #ClutterTexture object. + * Gets the filter quality used when scaling a texture. * - * Return value: A newly created #ClutterTexture object. + * Return value: The filter quality value. + * + * Since: 0.8 + */ +guint +clutter_texture_get_filter_quality (ClutterTexture *texture) +{ + ClutterTexturePrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), 0); + + priv = texture->priv; + + if (priv->texture == COGL_INVALID_HANDLE) + return texture->priv->max_tile_waste; + else + /* If we have a valid texture handle then use the filter quality + from that instead */ + return cogl_texture_get_min_filter (texture->priv->texture) + == CGL_LINEAR ? 1 : 0; +} + +/** + * clutter_texture_set_max_tile_waste + * @texture: A #ClutterTexture + * @max_tile_waste: Maximum amount of waste in pixels or -1 + * + * Sets the maximum number of pixels in either axis that can be wasted + * for an individual texture slice. If -1 is specified then the + * texture is forced not to be sliced and the texture creation will + * fail if the hardware can't create a texture large enough. + * + * The value is only used when first creating a texture so changing it + * after the texture data has been set has no effect. + * + * Since: 0.8 + */ +void +clutter_texture_set_max_tile_waste (ClutterTexture *texture, + gint max_tile_waste) +{ + ClutterTexturePrivate *priv; + + g_return_if_fail (CLUTTER_IS_TEXTURE (texture)); + + priv = texture->priv; + + /* There's no point in changing the max_tile_waste if the texture + has already been created because it will be overridden with the + value from the texture handle */ + if (priv->texture == COGL_INVALID_HANDLE) + priv->max_tile_waste = max_tile_waste; +} + +/** + * clutter_texture_get_max_tile_waste + * @texture: A #ClutterTexture + * + * Gets the maximum waste that will be used when creating a texture or + * -1 if slicing is disabled. + * + * Return value: The maximum waste or -1 if the texture waste is + * unlimited. + * + * Since: 0.8 + */ +gint +clutter_texture_get_max_tile_waste (ClutterTexture *texture) +{ + ClutterTexturePrivate *priv; + + g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), 0); + + priv = texture->priv; + + if (priv->texture == COGL_INVALID_HANDLE) + return texture->priv->max_tile_waste; + else + /* If we have a valid texture handle then use the value from that + instead */ + return cogl_texture_get_max_waste (texture->priv->texture); +} + +/** + * clutter_texture_new_from_file: + * @filename: The name of an image file to load. + * @error: Return locatoin for an error. + * + * Creates a new ClutterTexture actor to display the image contained a + * file. If the image failed to load then NULL is returned and @error + * is set. + * + * Return value: A newly created #ClutterTexture object or NULL on + * error. + * + * Since: 0.8 **/ ClutterActor* -clutter_texture_new_from_pixbuf (GdkPixbuf *pixbuf) +clutter_texture_new_from_file (const gchar *filename, + GError **error) { - return g_object_new (CLUTTER_TYPE_TEXTURE, "pixbuf", pixbuf, NULL); + ClutterActor *texture = clutter_texture_new (); + + if (!clutter_texture_set_from_file (CLUTTER_TEXTURE (texture), + filename, error)) + { + g_object_ref_sink (texture); + g_object_unref (texture); + + return NULL; + } + else + return texture; } /** @@ -1799,351 +1354,6 @@ clutter_texture_get_base_size (ClutterTexture *texture, if (height) *height = texture->priv->height; - -} - -/** - * clutter_texture_bind_tile: - * @texture: A #ClutterTexture - * @index_: Tile index to bind - * - * Proxies a call to glBindTexture to bind an internal 'tile'. - * - * This function is only useful for #ClutterTexture sub-classes - * and should never be called by an application. - **/ -void -clutter_texture_bind_tile (ClutterTexture *texture, - gint index_) -{ - ClutterTexturePrivate *priv; - - g_return_if_fail (CLUTTER_IS_TEXTURE (texture)); - - priv = texture->priv; - - cogl_texture_bind (priv->target_type, priv->tiles[index_]); -} - -/** - * clutter_texture_get_n_tiles: - * @texture: A #ClutterTexture - * @n_x_tiles: Location to store number of tiles in horizonally axis - * @n_y_tiles: Location to store number of tiles in vertical axis - * - * Retreives internal tile dimensioning. - * - * This function is only useful for #ClutterTexture sub-classes - * and should never be called by an application. - **/ -void -clutter_texture_get_n_tiles (ClutterTexture *texture, - gint *n_x_tiles, - gint *n_y_tiles) -{ - g_return_if_fail (CLUTTER_IS_TEXTURE (texture)); - - if (n_x_tiles) - *n_x_tiles = texture->priv->n_x_tiles; - - if (n_y_tiles) - *n_y_tiles = texture->priv->n_y_tiles; - -} - -/** - * clutter_texture_get_x_tile_detail: - * @texture: A #ClutterTexture - * @x_index: X index of tile to query - * @pos: Location to store tile's X position - * @size: Location to store tile's horizontal size in pixels - * @waste: Location to store tile's horizontal wastage in pixels - * - * Retreives details of a tile on the X axis. - * - * This function is only useful for #ClutterTexture sub-classes - * and should never be called by an application. - **/ -void -clutter_texture_get_x_tile_detail (ClutterTexture *texture, - gint x_index, - gint *pos, - gint *size, - gint *waste) -{ - ClutterTexturePrivate *priv; - - g_return_if_fail (CLUTTER_IS_TEXTURE (texture)); - g_return_if_fail (x_index < texture->priv->n_x_tiles); - - priv = texture->priv; - - if (pos) - *pos = priv->x_tiles[x_index].pos; - - if (size) - *size = priv->x_tiles[x_index].size; - - if (waste) - *waste = priv->x_tiles[x_index].waste; -} - -/** - * clutter_texture_get_y_tile_detail: - * @texture: A #ClutterTexture - * @y_index: Y index of tile to query - * @pos: Location to store tile's Y position - * @size: Location to store tile's vertical size in pixels - * @waste: Location to store tile's vertical wastage in pixels - * - * Retreives details of a tile on the Y axis. - * - * This function is only useful for #ClutterTexture sub-classes - * and should never be called by an application. - **/ -void -clutter_texture_get_y_tile_detail (ClutterTexture *texture, - gint y_index, - gint *pos, - gint *size, - gint *waste) -{ - ClutterTexturePrivate *priv; - - g_return_if_fail (CLUTTER_IS_TEXTURE (texture)); - - priv = texture->priv; - - g_return_if_fail (y_index < priv->n_y_tiles); - - if (pos) - *pos = priv->y_tiles[y_index].pos; - - if (size) - *size = priv->y_tiles[y_index].size; - - if (waste) - *waste = priv->y_tiles[y_index].waste; -} - -/** - * clutter_texture_has_generated_tiles: - * @texture: A #ClutterTexture - * - * Checks if #ClutterTexture has generated underlying GL texture tiles. - * - * This function is only useful for #ClutterTexture sub-classes - * and should never be called by an application. - * - * Return value: %TRUE if texture has pre-generated GL tiles. - **/ -gboolean -clutter_texture_has_generated_tiles (ClutterTexture *texture) -{ - g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE); - - return texture->priv->tiles != NULL; -} - -/** - * clutter_texture_is_tiled: - * @texture: A #ClutterTexture - * - * Checks if #ClutterTexture is tiled. - * - * This function is only useful for #ClutterTexture sub-classes - * and should never be called by an application. - * - * Return value: %TRUE if texture is tiled - **/ -gboolean -clutter_texture_is_tiled (ClutterTexture *texture) -{ - g_return_val_if_fail (CLUTTER_IS_TEXTURE (texture), FALSE); - - return texture->priv->is_tiled; -} - - -static void inline -texture_update_data (ClutterTexture *texture, - const guchar *data, - gboolean has_alpha, - gint x_0, - gint y_0, - gint width, - gint height, - gint rowstride, - gint bpp) -{ - ClutterTexturePrivate *priv; - gint x, y; - gint i = 0; - gboolean create_textures = FALSE; - - priv = texture->priv; - - g_return_if_fail (data != NULL); - - CLUTTER_MARK(); - - if (!priv->is_tiled) - { - g_assert (priv->tiles); - - CLUTTER_NOTE (TEXTURE, "syncing for single tile"); - - cogl_texture_bind (priv->target_type, priv->tiles[0]); - cogl_texture_set_alignment (priv->target_type, 4, rowstride/4); - - cogl_texture_set_filters - (priv->target_type, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST); - - cogl_texture_set_wrap (priv->target_type, - priv->repeat_x ? CGL_REPEAT : CGL_CLAMP_TO_EDGE, - priv->repeat_y ? CGL_REPEAT : CGL_CLAMP_TO_EDGE); - - priv->filter_quality = 1; - - cogl_texture_sub_image_2d (priv->target_type, - x_0, - y_0, - width, - height, - priv->pixel_format, - priv->pixel_type, - data); - return; - } - - /* Multiple tiled texture */ - - CLUTTER_NOTE (TEXTURE, - "syncing for multiple tiles for %ix%i pixbuf", - priv->width, priv->height); - - g_return_if_fail (priv->x_tiles != NULL && priv->y_tiles != NULL); - - if (priv->tiles == NULL) - { - g_assert (0); - priv->tiles = g_new (COGLuint, priv->n_x_tiles * priv->n_y_tiles); - glGenTextures (priv->n_x_tiles * priv->n_y_tiles, priv->tiles); - create_textures = TRUE; - } - - for (x = 0; x < priv->n_x_tiles; x++) - for (y = 0; y < priv->n_y_tiles; y++) - { - gint master_offset_x; - gint effective_x; - gint effective_width; - - gint master_offset_y; - gint effective_y; - gint effective_height; - -/* - * -- first tile -- - * |--------------------- priv->width ------------------------------| - * | <- priv->x_tiles[x].pos - * |-----------| <- priv->x_tiles[x].size - * |-------| <- x_0 - * |------------| <- width - * |--------------------| <- x_0 + width - * |-------| <- master_offset = -8 - * |-------| <- effective_x = 8 - * |---| <- effective_width - * - * -- second tile --- - * - * |--------------------- priv->width ------------------------------| - * |-----------| <- priv->x_tiles[x].pos - * |-----------| <- priv->x_tiles[x].size (src_w) - * |-------| <- x_0 - * |------------| <- width - * |--------------------| <- x_0 + width - * |---| <- master_offset = 4 - * | <- effective_x (0 in between) - * |--------| <- effective_width - * - * XXXXXXXXXXXXXX <- master - * |___________|___________|___________|___________|___________|_____%%%%%%| - */ - - gint src_w, src_h; - - src_w = priv->x_tiles[x].size; - src_h = priv->y_tiles[y].size; - - /* skip tiles that do not intersect the updated region */ - if ((priv->x_tiles[x].pos + src_w < x_0 || - priv->y_tiles[y].pos + src_h < y_0 || - priv->x_tiles[x].pos >= x_0 + width || - priv->y_tiles[y].pos >= y_0 + height)) - { - i++; - continue; - } - - master_offset_x = priv->x_tiles[x].pos - x_0; - - if (priv->x_tiles[x].pos > x_0) - effective_x = 0; - else - effective_x = x_0 - priv->x_tiles[x].pos; - - effective_width = (x_0 + width) - priv->x_tiles[x].pos; - - if (effective_width > src_w - effective_x) - effective_width = src_w - effective_x; - - master_offset_y = priv->y_tiles[y].pos - y_0; - - if (priv->y_tiles[y].pos > y_0) - effective_y = 0; - else - effective_y = y_0 - priv->y_tiles[y].pos; - - effective_height = (y_0 + height) - priv->y_tiles[y].pos; - if (effective_height > src_h - effective_y) - effective_height = src_h - effective_y; - - if (master_offset_x < 0) - master_offset_x = 0; - if (master_offset_y < 0) - master_offset_y = 0; - - if (master_offset_x + effective_width > width) - effective_width = width - master_offset_x; - if (master_offset_y + effective_height > height) - effective_height = height - master_offset_y; - - cogl_texture_bind (priv->target_type, priv->tiles[i]); - - cogl_texture_set_alignment (priv->target_type, 4, rowstride/4); - - cogl_texture_set_filters - (priv->target_type, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST, - priv->filter_quality ? CGL_LINEAR : CGL_NEAREST); - - cogl_texture_set_wrap (priv->target_type, - priv->repeat_x ? CGL_REPEAT : CGL_CLAMP_TO_EDGE, - priv->repeat_y ? CGL_REPEAT : CGL_CLAMP_TO_EDGE); - cogl_texture_sub_image_2d (priv->target_type, - effective_x, - effective_y, - effective_width, - effective_height, - priv->pixel_format, - priv->pixel_type, - data + (master_offset_y * priv->width + master_offset_x) * 4); - - i++; - } } /** @@ -2180,26 +1390,60 @@ clutter_texture_set_area_from_rgb_data (ClutterTexture *texture, GError **error) { ClutterTexturePrivate *priv; - guchar *copy_data = NULL; + CoglPixelFormat source_format; priv = texture->priv; - if (!texture_prepare_upload (FALSE, texture, data, has_alpha, - width, height, rowstride, - bpp, flags, ©_data, NULL, NULL)) + if (has_alpha) { + if (bpp != 4) + { + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "Unsupported BPP"); + return FALSE; + } + source_format = COGL_PIXEL_FORMAT_RGBA_8888; + } + else + { + if (bpp != 3) + { + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "Unsupported BPP"); + return FALSE; + } + source_format = COGL_PIXEL_FORMAT_RGB_888; + } + if ((flags & CLUTTER_TEXTURE_RGB_FLAG_BGR)) + source_format |= COGL_BGR_BIT; + if ((flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT)) + source_format |= COGL_PREMULT_BIT; + + clutter_actor_realize (CLUTTER_ACTOR (texture)); + + if (priv->texture == COGL_INVALID_HANDLE) + { + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "Failed to realize actor"); return FALSE; } - texture_update_data (texture, - copy_data != NULL ? copy_data : data, - has_alpha, - x, y, - width, height, - rowstride, - bpp); - - CLUTTER_ACTOR_SET_FLAGS (CLUTTER_ACTOR (texture), CLUTTER_ACTOR_REALIZED); + if (!cogl_texture_set_region (priv->texture, + 0, 0, + x, y, width, height, + width, height, + source_format, + rowstride, + data)) + { + g_set_error (error, CLUTTER_TEXTURE_ERROR, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT, + "Failed to upload COGL texture data"); + return FALSE; + } /* rename signal */ g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0); @@ -2207,9 +1451,6 @@ clutter_texture_set_area_from_rgb_data (ClutterTexture *texture, if (CLUTTER_ACTOR_IS_VISIBLE (texture)) clutter_actor_queue_redraw (CLUTTER_ACTOR (texture)); - if (copy_data != NULL) - g_free (copy_data); - return TRUE; } @@ -2225,36 +1466,34 @@ on_fbo_source_size_change (GObject *object, if (w != priv->width || h != priv->height) { - if (!cogl_texture_can_size (CGL_TEXTURE_RECTANGLE_ARB, - CGL_RGBA, PIXEL_TYPE, w, h)) - { - g_warning ("%s: Offscreen source too large for texture", G_STRLOC); - return; - } - /* tear down the FBO */ - cogl_offscreen_destroy (priv->fbo_handle); + cogl_offscreen_unref (priv->fbo_handle); texture_free_gl_resources (texture); priv->width = w; priv->height = h; - priv->tiles = g_new (COGLuint, 1); + priv->texture = cogl_texture_new_with_size (priv->width, + priv->height, + priv->max_tile_waste, + COGL_PIXEL_FORMAT_RGBA_8888); - /* FIXME: needs a cogl wrapper */ - glGenTextures (1, priv->tiles); + cogl_texture_set_filters (priv->texture, + priv->filter_quality + ? CGL_LINEAR : CGL_NEAREST, + priv->filter_quality + ? CGL_LINEAR : CGL_NEAREST); - cogl_texture_bind (priv->target_type, priv->tiles[0]); - cogl_texture_image_2d (priv->target_type, - CGL_RGBA, - w, - h, - priv->pixel_format, - priv->pixel_type, - NULL); + priv->fbo_handle = cogl_offscreen_new_to_texture (priv->texture); - priv->fbo_handle = cogl_offscreen_create (priv->tiles[0]); + if (priv->fbo_handle == COGL_INVALID_HANDLE) + { + g_warning ("%s: Offscreen texture creation failed", G_STRLOC); + CLUTTER_ACTOR_UNSET_FLAGS (CLUTTER_ACTOR (texture), + CLUTTER_ACTOR_REALIZED); + return; + } clutter_actor_set_size (CLUTTER_ACTOR(texture), w, h); } @@ -2276,7 +1515,6 @@ on_fbo_parent_change (ClutterActor *actor, } } - /** * clutter_texture_new_from_actor: * @actor: A source #ClutterActor @@ -2314,12 +1552,13 @@ on_fbo_parent_change (ClutterActor *actor, * * * Uploading pixel data to the texture (e.g by using - * clutter_actor_set_pixbuf()) will destroy the offscreen texture data + * clutter_actor_set_from_file()) will destroy the offscreen texture data * and end redirection. * * - * clutter_texture_get_pixbuf() can be used to read the offscreen - * texture pixels into a pixbuf. + * cogl_texture_get_data() with the handle returned by + * clutter_texture_get_cogl_texture() can be used to read the + * offscreen texture pixels into a pixbuf. * * * @@ -2356,10 +1595,6 @@ clutter_texture_new_from_actor (ClutterActor *actor) if (w == 0 || h == 0) return NULL; - if (!cogl_texture_can_size (CGL_TEXTURE_RECTANGLE_ARB, - CGL_RGBA, PIXEL_TYPE, w, h)) - return NULL; - /* Hopefully now were good.. */ texture = g_object_new (CLUTTER_TYPE_TEXTURE, NULL); @@ -2405,10 +1640,6 @@ clutter_texture_new_from_actor (ClutterActor *actor) priv->width = w; priv->height = h; - priv->target_type = CGL_TEXTURE_RECTANGLE_ARB; - priv->pixel_format = CGL_RGBA; - priv->pixel_type = PIXEL_TYPE; - priv->is_tiled = 0; clutter_actor_set_size (CLUTTER_ACTOR(texture), priv->width, priv->height); @@ -2441,10 +1672,25 @@ texture_fbo_free_resources (ClutterTexture *texture) priv->fbo_source = NULL; } - if (priv->fbo_handle != 0) + if (priv->fbo_handle != COGL_INVALID_HANDLE) { - cogl_offscreen_destroy (priv->fbo_handle); - priv->fbo_handle = 0; + cogl_offscreen_unref (priv->fbo_handle); + priv->fbo_handle = COGL_INVALID_HANDLE; } } +GType +clutter_texture_handle_get_type (void) +{ + static GType our_type = 0; + + if (G_UNLIKELY (!our_type)) + { + our_type = + g_boxed_type_register_static (I_("ClutterTextureHandle"), + (GBoxedCopyFunc) cogl_texture_ref, + (GBoxedFreeFunc) cogl_texture_unref); + } + + return our_type; +} diff --git a/clutter/clutter-texture.h b/clutter/clutter-texture.h index 8aeb74959..6ae700703 100644 --- a/clutter/clutter-texture.h +++ b/clutter/clutter-texture.h @@ -27,7 +27,7 @@ #define _HAVE_CLUTTER_TEXTURE_H #include -#include +#include G_BEGIN_DECLS @@ -38,11 +38,16 @@ G_BEGIN_DECLS #define CLUTTER_IS_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TEXTURE)) #define CLUTTER_TEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TEXTURE, ClutterTextureClass)) +#define CLUTTER_TYPE_TEXTURE_HANDLE (clutter_texture_handle_get_type ()) + /** * ClutterTextureError: * @CLUTTER_TEXTURE_ERROR_OUT_OF_MEMORY: OOM condition * @CLUTTER_TEXTURE_ERROR_NO_YUV: YUV operation attempted but no YUV support * found + * @CLUTTER_TEXTURE_ERROR_BAD_FORMAT: The requested format for + * clutter_texture_set_from_rgb_data or + * clutter_texture_set_from_yuv_data is unsupported. * * Error enumeration for #ClutterTexture * @@ -50,7 +55,8 @@ G_BEGIN_DECLS */ typedef enum { CLUTTER_TEXTURE_ERROR_OUT_OF_MEMORY, - CLUTTER_TEXTURE_ERROR_NO_YUV + CLUTTER_TEXTURE_ERROR_NO_YUV, + CLUTTER_TEXTURE_ERROR_BAD_FORMAT } ClutterTextureError; #define CLUTTER_TEXTURE_ERROR (clutter_texture_error_quark ()) @@ -107,9 +113,14 @@ typedef enum { /*< prefix=CLUTTER_TEXTURE >*/ } ClutterTextureFlags; GType clutter_texture_get_type (void) G_GNUC_CONST; +GType clutter_texture_handle_get_type (void) G_GNUC_CONST; ClutterActor *clutter_texture_new (void); -ClutterActor *clutter_texture_new_from_pixbuf (GdkPixbuf *pixbuf); +ClutterActor *clutter_texture_new_from_file (const gchar *filename, + GError **error); +gboolean clutter_texture_set_from_file (ClutterTexture *texture, + const gchar *filename, + GError **error); gboolean clutter_texture_set_from_rgb_data (ClutterTexture *texture, const guchar *data, gboolean has_alpha, @@ -138,33 +149,19 @@ gboolean clutter_texture_set_from_yuv_data (ClutterTexture *texture, gint height, ClutterTextureFlags flags, GError **error); -gboolean clutter_texture_set_pixbuf (ClutterTexture *texture, - GdkPixbuf *pixbuf, - GError **error); -GdkPixbuf * clutter_texture_get_pixbuf (ClutterTexture *texture); void clutter_texture_get_base_size (ClutterTexture *texture, gint *width, gint *height); +void clutter_texture_set_filter_quality (ClutterTexture *texture, + guint filter_quality); +guint clutter_texture_get_filter_quality (ClutterTexture *texture); +void clutter_texture_set_max_tile_waste (ClutterTexture *texture, + gint max_tile_waste); +gint clutter_texture_get_max_tile_waste (ClutterTexture *texture); -/* Below mainly for subclassed texture based actors */ - -void clutter_texture_bind_tile (ClutterTexture *texture, - gint index_); -void clutter_texture_get_n_tiles (ClutterTexture *texture, - gint *n_x_tiles, - gint *n_y_tiles); -void clutter_texture_get_x_tile_detail (ClutterTexture *texture, - gint x_index, - gint *pos, - gint *size, - gint *waste); -void clutter_texture_get_y_tile_detail (ClutterTexture *texture, - gint y_index, - gint *pos, - gint *size, - gint *waste); -gboolean clutter_texture_has_generated_tiles (ClutterTexture *texture); -gboolean clutter_texture_is_tiled (ClutterTexture *texture); +CoglHandle clutter_texture_get_cogl_texture (ClutterTexture *texture); +void clutter_texture_set_cogl_texture (ClutterTexture *texture, + CoglHandle cogl_tex); G_END_DECLS diff --git a/clutter/cogl/Makefile.am b/clutter/cogl/Makefile.am index f09f63723..9bb6b772c 100644 --- a/clutter/cogl/Makefile.am +++ b/clutter/cogl/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = $(CLUTTER_COGL) +SUBDIRS = common $(CLUTTER_COGL) EXTRA_DIST = cogl.h diff --git a/clutter/cogl/TODO b/clutter/cogl/TODO new file mode 100644 index 000000000..e1e5d316c --- /dev/null +++ b/clutter/cogl/TODO @@ -0,0 +1,25 @@ +============================ +Cogl overhaul related tasks: +============================ + +MISC + +- implemenent a 1 to 1 mapping of COGL_FEATURE flags + into CLUTTER_FEATURE flags before combining them + into final clutter flags value (clutter-feature.c) + +TEXTURE + +- cogl_texture_get_data, cogl_texture_set_region in GLES + +- YUV texture format support (multitexturing + shader) + +FBO + +- add stencil, depth and other renderbuffers to fbos + +- cogl_offscreen_new_multisample + +- test cogl_offscreen_blit + +- add "filter" argument to cogl_offscreen_blit diff --git a/clutter/cogl/cogl.h b/clutter/cogl/cogl.h deleted file mode 100644 index beb2955d7..000000000 --- a/clutter/cogl/cogl.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Clutter COGL - * - * A basic GL/GLES Abstraction/Utility Layer - * - * Authored By Matthew Allum - * - * Copyright (C) 2007 OpenedHand - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* - * COGL - * ==== - * - * 'cogl' is a very simple abstraction layer which wraps GL and GLES. - * - * - * !!!! DO NOT USE THIS API YET OUTSIDE OF CLUTTER CORE !!!! - * THE API WILL FLUCTUATE WILDLY - * - * TODO: - * - Use ClutterReal for fixed/float params. - * - Add Perspective/viewport setup - * - Add Features.. - */ - -#ifndef __COGL_H__ -#define __COGL_H__ - -#include -#include -#include -#include -#include - -#include "cogl-defines.h" - -G_BEGIN_DECLS - -#define CGL_ENABLE_BLEND (1<<1) -#define CGL_ENABLE_TEXTURE_2D (1<<2) -#define CGL_ENABLE_ALPHA_TEST (1<<3) -#define CGL_ENABLE_TEXTURE_RECT (1<<4) - -typedef void (*CoglFuncPtr) (void); - -CoglFuncPtr -cogl_get_proc_address (const gchar* name); - -gboolean -cogl_check_extension (const gchar *name, const gchar *ext); - -void -cogl_perspective (ClutterFixed fovy, - ClutterFixed aspect, - ClutterFixed zNear, - ClutterFixed zFar); - -void -cogl_setup_viewport (guint width, - guint height, - ClutterFixed fovy, - ClutterFixed aspect, - ClutterFixed z_near, - ClutterFixed z_far); - -void -cogl_paint_init (const ClutterColor *color); - -void -cogl_push_matrix (void); - -void -cogl_pop_matrix (void); - -void -cogl_scale (ClutterFixed x, ClutterFixed z); - -void -cogl_translatex (ClutterFixed x, ClutterFixed y, ClutterFixed z); - -void -cogl_translate (gint x, gint y, gint z); - -void -cogl_rotatex (ClutterFixed angle, gint x, gint y, gint z); - -void -cogl_rotate (gint angle, gint x, gint y, gint z); - -void -cogl_color (const ClutterColor *color); - -void -cogl_clip_set (ClutterFixed x_offset, - ClutterFixed y_offset, - ClutterFixed width, - ClutterFixed height); - -void -cogl_clip_unset (void); - -void -cogl_enable (gulong flags); - -void -cogl_enable_depth_test (gboolean setting); - -gboolean -cogl_texture_can_size (COGLenum target, - COGLenum pixel_format, - COGLenum pixel_type, - int width, - int height); - -void -cogl_texture_quad (gint x1, - gint x2, - gint y1, - gint y2, - ClutterFixed tx1, - ClutterFixed ty1, - ClutterFixed tx2, - ClutterFixed ty2); - -void -cogl_textures_create (guint num, COGLuint *textures); - -void -cogl_textures_destroy (guint num, const COGLuint *textures); - -void -cogl_texture_bind (COGLenum target, COGLuint texture); - -void -cogl_texture_set_alignment (COGLenum target, - guint alignment, - guint row_length); - -void -cogl_texture_set_filters (COGLenum target, - COGLenum min_filter, - COGLenum max_filter); - -void -cogl_texture_set_wrap (COGLenum target, - COGLenum wrap_s, - COGLenum wrap_t); - -void -cogl_texture_image_2d (COGLenum target, - COGLint internal_format, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels); - -void -cogl_texture_sub_image_2d (COGLenum target, - gint xoff, - gint yoff, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels); - -void -cogl_rectangle (gint x, gint y, guint width, guint height); - -void -cogl_trapezoid (gint y1, - gint x11, - gint x21, - gint y2, - gint x12, - gint x22); -void -cogl_alpha_func (COGLenum func, - ClutterFixed ref); - -ClutterFeatureFlags -cogl_get_features (); - -void -cogl_get_modelview_matrix (ClutterFixed m[16]); - -void -cogl_get_projection_matrix (ClutterFixed m[16]); - -void -cogl_get_viewport (ClutterFixed v[4]); - -void -cogl_get_bitmasks (gint *red, gint *green, gint *blue, gint *alpha); - -void -cogl_fog_set (const ClutterColor *fog_color, - ClutterFixed density, - ClutterFixed z_near, - ClutterFixed z_far); - - -COGLhandle -cogl_create_shader (COGLenum shaderType); - -void -cogl_shader_destroy (COGLhandle handle); - - -void -cogl_shader_source (COGLhandle shader, - const gchar *source); -void -cogl_shader_compile (COGLhandle shader_handle); - -void -cogl_shader_get_info_log (COGLhandle handle, - guint size, - gchar *buffer); - -void -cogl_shader_get_parameteriv (COGLhandle handle, - COGLenum pname, - COGLint *dest); - - -COGLhandle -cogl_create_program (void); - -void -cogl_program_destroy (COGLhandle handle); - -void -cogl_program_attach_shader (COGLhandle program_handle, - COGLhandle shader_handle); - -/* 0 to use none */ -void -cogl_program_link (COGLhandle program_handle); - -void -cogl_program_use (COGLhandle program_handle); - -COGLint -cogl_program_get_uniform_location (COGLhandle program_int, - const gchar *uniform_name); - - -void -cogl_program_uniform_1f (COGLint uniform_no, - gfloat value); - -/* Offscreen - FBO support */ - -COGLuint -cogl_offscreen_create (COGLuint target_texture); - -void -cogl_offscreen_destroy (COGLuint offscreen_handle); - -void -cogl_offscreen_redirect_start (COGLuint offscreen_handle, - gint width, - gint height); - -void -cogl_offscreen_redirect_end (COGLuint offscreen_handle, - gint width, - gint height); - -G_END_DECLS - -#endif /* __COGL_H__ */ diff --git a/clutter/cogl/cogl.h.in b/clutter/cogl/cogl.h.in new file mode 100644 index 000000000..47b6b58f3 --- /dev/null +++ b/clutter/cogl/cogl.h.in @@ -0,0 +1,874 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * COGL + * ==== + * + * 'cogl' is a very simple abstraction layer which wraps GL and GLES. + * + * + * !!!! DO NOT USE THIS API YET OUTSIDE OF CLUTTER CORE !!!! + * THE API WILL FLUCTUATE WILDLY + * + * TODO: + * - Use ClutterReal for fixed/float params. + * - Add Perspective/viewport setup + * - Add Features.. + */ + +#ifndef __COGL_H__ +#define __COGL_H__ + +#include +#include +#include +#include +#include + +#include + +G_BEGIN_DECLS + +/* Enum declarations */ + +#define COGL_PIXEL_FORMAT_24 2 +#define COGL_PIXEL_FORMAT_32 3 +#define COGL_A_BIT (1 << 4) +#define COGL_BGR_BIT (1 << 5) +#define COGL_AFIRST_BIT (1 << 6) +#define COGL_PREMULT_BIT (1 << 7) +#define COGL_UNORDERED_MASK 0x0F +#define COGL_UNPREMULT_MASK 0x7F + +/** + * CoglPixelFormat: + * @COGL_PIXEL_FORMAT_ANY: + * @COGL_PIXEL_FORMAT_A_8: + * @COGL_PIXEL_FORMAT_RGB_888: + * @COGL_PIXEL_FORMAT_BGR_888: + * @COGL_PIXEL_FORMAT_RGBA_8888: + * @COGL_PIXEL_FORMAT_BGRA_8888: + * @COGL_PIXEL_FORMAT_ARGB_8888: + * @COGL_PIXEL_FORMAT_ABGR_8888: + * @COGL_PIXEL_FORMAT_RGBA_8888_PRE: + * @COGL_PIXEL_FORMAT_BGRA_8888_PRE: + * @COGL_PIXEL_FORMAT_ARGB_8888_PRE: + * @COGL_PIXEL_FORMAT_ABGR_8888_PRE: + * @COGL_PIXEL_FORMAT_RGB_565: + * @COGL_PIXEL_FORMAT_RGBA_4444: + * @COGL_PIXEL_FORMAT_RGBA_5551: + * @COGL_PIXEL_FORMAT_RGBA_4444_PRE: + * @COGL_PIXEL_FORMAT_RGBA_5551_PRE: + * @COGL_PIXEL_FORMAT_YUV: + * @COGL_PIXEL_FORMAT_G_8: + * + * Pixel formats used by COGL. + */ +typedef enum +{ + COGL_PIXEL_FORMAT_ANY = 0, + COGL_PIXEL_FORMAT_A_8 = 1 | COGL_A_BIT, + + COGL_PIXEL_FORMAT_RGB_888 = COGL_PIXEL_FORMAT_24, + COGL_PIXEL_FORMAT_BGR_888 = (COGL_PIXEL_FORMAT_24 | COGL_BGR_BIT), + + COGL_PIXEL_FORMAT_RGBA_8888 = COGL_PIXEL_FORMAT_32 | COGL_A_BIT, + COGL_PIXEL_FORMAT_BGRA_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_BGR_BIT), + COGL_PIXEL_FORMAT_ARGB_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_AFIRST_BIT), + COGL_PIXEL_FORMAT_ABGR_8888 = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), + + COGL_PIXEL_FORMAT_RGBA_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_BGRA_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT), + COGL_PIXEL_FORMAT_ARGB_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_AFIRST_BIT), + COGL_PIXEL_FORMAT_ABGR_8888_PRE = (COGL_PIXEL_FORMAT_32 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), + + COGL_PIXEL_FORMAT_RGB_565 = 4, + COGL_PIXEL_FORMAT_RGBA_4444 = 5 | COGL_A_BIT, + COGL_PIXEL_FORMAT_RGBA_5551 = 6 | COGL_A_BIT, + + COGL_PIXEL_FORMAT_RGBA_4444_PRE = (COGL_PIXEL_FORMAT_RGBA_4444 | COGL_A_BIT | COGL_PREMULT_BIT), + COGL_PIXEL_FORMAT_RGBA_5551_PRE = (COGL_PIXEL_FORMAT_RGBA_5551 | COGL_A_BIT | COGL_PREMULT_BIT), + + COGL_PIXEL_FORMAT_YUV = 7, + + COGL_PIXEL_FORMAT_G_8 = 8 + +} CoglPixelFormat; + +/** + * CoglFeatureFlags: + * @COGL_FEATURE_TEXTURE_RECTANGLE: + * @COGL_FEATURE_TEXTURE_NPOT: + * @COGL_FEATURE_TEXTURE_YUV: + * @COGL_FEATURE_TEXTURE_READ_PIXELS: + * @COGL_FEATURE_SHADERS_GLSL: + * @COGL_FEATURE_OFFSCREEN: + * @COGL_FEATURE_OFFSCREEN_MULTISAMPLE: + * @COGL_FEATURE_OFFSCREEN_BLIT: + * @COGL_FEATURE_FOUR_CLIP_PLANES: + * @COGL_FEATURE_STENCIL_BUFFER: + * + * Flags for the supported features. + */ +typedef enum +{ + COGL_FEATURE_TEXTURE_RECTANGLE = (1 << 1), + COGL_FEATURE_TEXTURE_NPOT = (1 << 2), + COGL_FEATURE_TEXTURE_YUV = (1 << 3), + COGL_FEATURE_TEXTURE_READ_PIXELS = (1 << 4), + COGL_FEATURE_SHADERS_GLSL = (1 << 5), + COGL_FEATURE_OFFSCREEN = (1 << 6), + COGL_FEATURE_OFFSCREEN_MULTISAMPLE = (1 << 7), + COGL_FEATURE_OFFSCREEN_BLIT = (1 << 8), + COGL_FEATURE_FOUR_CLIP_PLANES = (1 << 9), + COGL_FEATURE_STENCIL_BUFFER = (1 << 10) + +} CoglFeatureFlags; + +/** + * CoglBufferTarget: + * @COGL_WINDOW_BUFFER: + * @COGL_MASK_BUFFER: + * @COGL_OFFSCREEN_BUFFER: + * + * FIXME + */ +typedef enum +{ + COGL_WINDOW_BUFFER = (1 << 1), + COGL_MASK_BUFFER = (1 << 2), + COGL_OFFSCREEN_BUFFER = (1 << 3) + +} CoglBufferTarget; + +/** + * CoglTextureVertex: + * @x: Model x-coordinate + * @y: Model y-coordinate + * @z: Model z-coordinate + * @tx: Texture x-coordinate + * @ty: Texture y-coordinate + * @color: The color to use at this vertex. This is ignored if + * @use_color is %FALSE when calling cogl_texture_polygon(). + * + * Used to specify vertex information when calling cogl_texture_polygon(). + */ +struct _CoglTextureVertex +{ + ClutterFixed x, y, z; + ClutterFixed tx, ty; + ClutterColor color; +}; + +typedef struct _CoglTextureVertex CoglTextureVertex; + +/* Context manipulation */ + +gboolean +cogl_create_context (void); + +void +cogl_destroy_context (void); + +/* Misc */ +#define COGL_INVALID_HANDLE NULL + +typedef gpointer CoglHandle; + +typedef void (* CoglFuncPtr) (void); + +/** + * cogl_get_features: + * + * Returns all of the features supported by COGL. + * + * Return value: A logical OR of all the supported COGL features. + * + * Since: 0.8 + */ +ClutterFeatureFlags +cogl_get_features (void); + +/** + * cogl_features_available: + * @features: A bitmask of features to check for + * + * Checks whether the given COGL features are available. Multiple + * features can be checked for by or-ing them together with the '|' + * operator. %TRUE is only returned if all of the requested features + * are available. + * + * Return value: %TRUE if the features are available, %FALSE otherwise. + */ +gboolean +cogl_features_available (CoglFeatureFlags features); + +/** + * cogl_get_proc_address: + * @name: the name of the function. + * + * Gets a pointer to a given GL or GL ES extension function. This acts + * as a wrapper around glXGetProcAddress() or whatever is the + * appropriate function for the current backend. + * + * Return value: a pointer to the requested function or %NULL if the + * function is not available. + */ +CoglFuncPtr +cogl_get_proc_address (const gchar* name); + +gboolean +cogl_check_extension (const gchar *name, + const gchar *ext); + +/** + * cogl_get_bitmasks: + * @red: Return location for the number of red bits or %NULL + * @green: Return location for the number of green bits or %NULL + * @blue: Return location for the number of blue bits or %NULL + * @alpha: Return location for the number of alpha bits or %NULL + * + * Gets the number of bitplanes used for each of the color components + * in the color buffer. Pass %NULL for any of the arguments if the + * value is not required. + */ +void +cogl_get_bitmasks (gint *red, + gint *green, + gint *blue, + gint *alpha); + +void +cogl_perspective (ClutterFixed fovy, + ClutterFixed aspect, + ClutterFixed zNear, + ClutterFixed zFar); + +/** + * cogl_setup_viewport: + * @width: Width of the viewport + * @height: Height of the viewport + * @fovy: Field of view angle in degrees + * @aspect: Aspect ratio to determine the field of view along the x-axis + * @z_near: Nearest visible point along the z-axis + * @z_far: Furthest visible point along the z-axis + * + * Replaces the current viewport and projection matrix with the given + * values. The viewport is placed at the top left corner of the window + * with the given width and height. The projection matrix is replaced + * with one that has a viewing angle of @fovy along the y-axis and a + * view scaled according to @aspect along the x-axis. The view is + * clipped according to @z_near and @z_far on the z-axis. + */ +void +cogl_setup_viewport (guint width, + guint height, + ClutterFixed fovy, + ClutterFixed aspect, + ClutterFixed z_near, + ClutterFixed z_far); + +/** + * cogl_push_matrix: + * + * Store the current model-view matrix on the matrix stack. The matrix + * can later be restored with cogl_pop_matrix(). + */ +void +cogl_push_matrix (void); + +/** + * cogl_pop_matrix: + * + * Restore the current model-view matrix from the matrix stack. + */ +void +cogl_pop_matrix (void); + +/** + * cogl_scale: + * @x: Amount to scale along the x-axis + * @y: Amount to scale along the y-axis + * + * Multiplies the current model-view matrix by one that scales the x + * and y axes by the given values. + */ +void +cogl_scale (ClutterFixed x, ClutterFixed y); + +/** + * cogl_translatex: + * @x: Distance to translate along the x-axis + * @y: Distance to translate along the y-axis + * @z: Distance to translate along the z-axis + * + * Multiplies the current model-view matrix by one that translates the + * model along all three axes according to the given values. + */ +void +cogl_translatex (ClutterFixed x, ClutterFixed y, ClutterFixed z); + +/** + * cogl_translate: + * @x: Distance to translate along the x-axis + * @y: Distance to translate along the y-axis + * @z: Distance to translate along the z-axis + * + * Integer version of cogl_translatex(). Multiplies the current + * model-view matrix by one that translates the model along all three + * axes according to the given values. + */ +void +cogl_translate (gint x, gint y, gint z); + +/** + * cogl_rotatex: + * @angle: Angle in degrees to rotate. + * @x: X-component of vertex to rotate around. + * @y: Y-component of vertex to rotate around. + * @z: Z-component of vertex to rotate around. + * + * Multiplies the current model-view matrix by one that rotates the + * model around the vertex specified by @x, @y and @z. The rotation + * follows the right-hand thumb rule so for example rotating by 10 + * degrees about the vertex (0, 0, 1) causes a small counter-clockwise + * rotation. + */ +void +cogl_rotatex (ClutterFixed angle, gint x, gint y, gint z); + +/** + * cogl_rotate: + * @angle: Angle in degrees to rotate. + * @x: X-component of vertex to rotate around. + * @y: Y-component of vertex to rotate around. + * @z: Z-component of vertex to rotate around. + * + * Integer version of cogl_rotatex(). Multiplies the current + * model-view matrix by one that rotates the model around the vertex + * specified by @x, @y and @z. + */ +void +cogl_rotate (gint angle, gint x, gint y, gint z); + +/** + * cogl_get_modelview_matrix: + * @m: pointer to a 4x4 array of #ClutterFixeds to receive the matrix + * + * Stores the current model-view matrix in @m. The matrix is in + * column-major order. + */ +void +cogl_get_modelview_matrix (ClutterFixed m[16]); + +/** + * cogl_get_projection_matrix: + * @m: pointer to a 4x4 array of #ClutterFixeds to receive the matrix + * + * Stores the current projection matrix in @m. The matrix is in + * column-major order. + */ +void +cogl_get_projection_matrix (ClutterFixed m[16]); + +/** + * cogl_get_viewport: + * @v: pointer to a 4 element array of #ClutterFixeds to + * receive the viewport dimensions. + * + * Stores the current viewport in @v. @v[0] and @v[1] get the x and y + * position of the viewport and @v[2] and @v[3] get the width and + * height. + */ +void +cogl_get_viewport (ClutterFixed v[4]); + +/** + * cogl_clip_set: + * @x_offset: left edge of the clip rectangle + * @y_offset: top edge of the clip rectangle + * @width: width of the clip rectangle + * @height: height of the clip rectangle + * + * Specifies a rectangular clipping area for all subsequent drawing + * operations. Any drawing commands that extend outside the rectangle + * will be clipped so that only the portion inside the rectangle will + * be displayed. The rectangle dimensions are transformed by the + * current model-view matrix. + */ +void +cogl_clip_set (ClutterFixed x_offset, + ClutterFixed y_offset, + ClutterFixed width, + ClutterFixed height); + +/** + * cogl_clip_unset: + * + * Removes the current clipping rectangle so that all drawing + * operations extend to full size of the viewport again. + */ +void +cogl_clip_unset (void); + +/** + * cogl_enable_depth_test: + * @setting: %TRUE to enable depth testing or %FALSE to disable. + * + * Sets whether depth testing is enabled. If it is disabled then the + * order that actors are layered on the screen depends solely on the + * order specified using clutter_actor_raise() and + * clutter_actor_lower(), otherwise it will also take into account the + * actor's depth. Depth testing is disabled by default. + */ +void +cogl_enable_depth_test (gboolean setting); + +void +cogl_alpha_func (COGLenum func, + ClutterFixed ref); + +/** + * cogl_fog_set: + * @fog_color: The color of the fog + * @density: Ignored + * @z_near: Position along z-axis where no fogging should be applied + * @z_far: Position along z-axes where full fogging should be applied + * + * Enables fogging. Fogging causes vertices that are further away from + * the eye to be rendered with a different color. The color is + * linearly interpolated so that vertices at @z_near are drawn fully + * with their original color and vertices at @z_far are drawn fully + * with @fog_color. Fogging will remain enabled until the next call to + * cogl_paint_init(). + */ +void +cogl_fog_set (const ClutterColor *fog_color, + ClutterFixed density, + ClutterFixed z_near, + ClutterFixed z_far); + +/** + * cogl_paint_init: + * @color: Background color to clear to + * + * Clears the color buffer to @color. The depth buffer and stencil + * buffers are also cleared and fogging and lighting are disabled. + */ +void +cogl_paint_init (const ClutterColor *color); + +/* Textures api */ + +CoglHandle +cogl_texture_new_with_size (guint width, + guint height, + gint max_waste, + CoglPixelFormat internal_format); + +CoglHandle +cogl_texture_new_from_file (const gchar *filename, + gint max_waste, + CoglPixelFormat internal_format, + GError **error); + +CoglHandle +cogl_texture_new_from_data (guint width, + guint height, + gint max_waste, + CoglPixelFormat format, + CoglPixelFormat internal_format, + guint rowstride, + const guchar *data); + +CoglHandle +cogl_texture_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format); + +/** + * cogl_is_texture: + * @handle: A CoglHandle + * + * Gets whether the given handle references an existing texture object. + * + * Return value: %TRUE if the handle references a texture, + * %FALSE otherwise + */ +gboolean +cogl_is_texture (CoglHandle handle); + +guint +cogl_texture_get_width (CoglHandle handle); + +guint +cogl_texture_get_height (CoglHandle handle); + +CoglPixelFormat +cogl_texture_get_format (CoglHandle handle); + +guint +cogl_texture_get_rowstride (CoglHandle handle); + +gint +cogl_texture_get_max_waste (CoglHandle handle); + +COGLenum +cogl_texture_get_min_filter (CoglHandle handle); + +COGLenum +cogl_texture_get_mag_filter (CoglHandle handle); + +gboolean +cogl_texture_is_sliced (CoglHandle handle); + +gboolean +cogl_texture_get_gl_texture (CoglHandle handle, + GLuint *out_gl_handle, + GLenum *out_gl_target); + +gint +cogl_texture_get_data (CoglHandle handle, + CoglPixelFormat format, + guint rowstride, + guchar *data); + +void +cogl_texture_set_filters (CoglHandle handle, + COGLenum min_filter, + COGLenum mag_filter); + +gboolean +cogl_texture_set_region (CoglHandle handle, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + guint dst_width, + guint dst_height, + gint width, + gint height, + CoglPixelFormat format, + guint rowstride, + const guchar *data); + +CoglHandle +cogl_texture_ref (CoglHandle handle); + +void +cogl_texture_unref (CoglHandle handle); + +void +cogl_texture_rectangle (CoglHandle handle, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2); + +/** + * cogl_texture_polygon: + * @handle: A CoglHandle for a texture + * @n_vertices: The length of the vertices array + * @vertices: An array of #CoglTextureVertex structs + * @use_color: %TRUE if the color member of #CoglTextureVertex should be used + * + * Draws a polygon from a texture with the given model and texture + * coordinates. This can be used to draw arbitrary shapes textured + * with a COGL texture. If @use_color is %TRUE then the current COGL + * color will be changed for each vertex using the value specified in + * the color member of #CoglTextureVertex. This can be used for + * example to make the texture fade out by setting the alpha value of + * the color. + * + * All of the texture coordinates must be in the range [0,1] and + * repeating the texture is not supported. + * + * Because of the way this function is implemented it will currently + * only work if either the texture is not sliced or the backend is not + * OpenGL ES and the minifying and magnifying functions are both set + * to CGL_NEAREST. + */ +void +cogl_texture_polygon (CoglHandle handle, + guint n_vertices, + CoglTextureVertex *vertices, + gboolean use_color); + +/* Primitives API */ + +void +cogl_color (const ClutterColor *color); + +void +cogl_fast_fill_rectangle (gint x, + gint y, + guint width, + guint height); + +void +cogl_fast_fill_rectanglex (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height); + +void +cogl_fast_fill_trapezoid (gint y1, + gint x11, + gint x21, + gint y2, + gint x12, + gint x22); + +void +cogl_fast_fill_trapezoidx (ClutterFixed y1, + ClutterFixed x11, + ClutterFixed x21, + ClutterFixed y2, + ClutterFixed x12, + ClutterFixed x22); + +void +cogl_fill (); + +void +cogl_stroke (); + +void +cogl_path_move_to (ClutterFixed x, + ClutterFixed y); + +void +cogl_path_move_to_rel (ClutterFixed x, + ClutterFixed y); + +void +cogl_path_line_to (ClutterFixed x, + ClutterFixed y); + +void +cogl_path_line_to_rel (ClutterFixed x, + ClutterFixed y); + +void +cogl_path_h_line_to (ClutterFixed x); + +void +cogl_path_v_line_to (ClutterFixed y); + +void +cogl_path_h_line_to_rel (ClutterFixed x); + +void +cogl_path_v_line_to_rel (ClutterFixed y); + +void +cogl_path_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step); + +void +cogl_path_arc_rel (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step); + +void +cogl_path_bezier2_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2); + +void +cogl_path_bezier2_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2); + +void +cogl_path_bezier3_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3); + +void +cogl_path_bezier3_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3); + +void +cogl_path_close (); + +void +cogl_line (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2); + +void +cogl_polyline (ClutterFixed *coords, + gint num_points); + +void +cogl_polygon (ClutterFixed *coords, + gint num_points); + +void +cogl_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height); + +void +cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step); + +void +cogl_ellipse (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_step); + +void +cogl_round_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height, + ClutterFixed radius, + ClutterAngle arc_step); + + +COGLhandle +cogl_create_shader (COGLenum shaderType); + +void +cogl_shader_destroy (COGLhandle handle); + + +void +cogl_shader_source (COGLhandle shader, + const gchar *source); +void +cogl_shader_compile (COGLhandle shader_handle); + +void +cogl_shader_get_info_log (COGLhandle handle, + guint size, + gchar *buffer); + +void +cogl_shader_get_parameteriv (COGLhandle handle, + COGLenum pname, + COGLint *dest); + + +COGLhandle +cogl_create_program (void); + +void +cogl_program_destroy (COGLhandle handle); + +void +cogl_program_attach_shader (COGLhandle program_handle, + COGLhandle shader_handle); + +/* 0 to use none */ +void +cogl_program_link (COGLhandle program_handle); + +void +cogl_program_use (COGLhandle program_handle); + +COGLint +cogl_program_get_uniform_location (COGLhandle program_int, + const gchar *uniform_name); + + +void +cogl_program_uniform_1f (COGLint uniform_no, + gfloat value); + +/* Offscreen api */ + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle); + +CoglHandle +cogl_offscreen_new_multisample (); + +CoglHandle +cogl_offscreen_ref (CoglHandle handle); + +void +cogl_offscreen_unref (CoglHandle handle); + +void +cogl_offscreen_blit (CoglHandle src_buffer, + CoglHandle dst_buffer); + +void +cogl_offscreen_blit_region (CoglHandle src_buffer, + CoglHandle dst_buffer, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h); + +void +cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen); + +G_END_DECLS + +#endif /* __COGL_H__ */ diff --git a/clutter/cogl/common/Makefile.am b/clutter/cogl/common/Makefile.am new file mode 100644 index 000000000..b6dca2df2 --- /dev/null +++ b/clutter/cogl/common/Makefile.am @@ -0,0 +1,23 @@ +INCLUDES = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/clutter \ + -I$(top_srcdir)/clutter/cogl \ + -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ + -I$(top_builddir)/clutter \ + -I$(top_builddir)/clutter/cogl \ + $(CLUTTER_CFLAGS) \ + $(CLUTTER_DEBUG_CFLAGS) \ + $(GCC_FLAGS) + +LDADD = $(CLUTTER_LIBS) + +noinst_LTLIBRARIES = libclutter-cogl-common.la +EXTRA_DIST = stb_image.c + +libclutter_cogl_common_la_SOURCES = \ + cogl-util.h \ + cogl-bitmap.h \ + cogl-util.c \ + cogl-bitmap.c \ + cogl-bitmap-fallback.c \ + cogl-bitmap-pixbuf.c diff --git a/clutter/cogl/common/cogl-bitmap-fallback.c b/clutter/cogl/common/cogl-bitmap-fallback.c new file mode 100644 index 000000000..21a558460 --- /dev/null +++ b/clutter/cogl/common/cogl-bitmap-fallback.c @@ -0,0 +1,369 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-bitmap.h" + +#include + +/* TO rgba */ + +inline static void +_cogl_g_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[0]; + dst[1] = src[0]; + dst[2] = src[0]; + dst[3] = 255; +} + +inline static void +_cogl_rgb_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = 255; +} + +inline static void +_cogl_bgr_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = 255; +} + +inline static void +_cogl_bgra_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[3]; +} + +inline static void +_cogl_argb_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[1]; + dst[1] = src[2]; + dst[2] = src[3]; + dst[3] = src[0]; +} + +inline static void +_cogl_abgr_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +inline static void +_cogl_rgba_to_rgba (const guchar *src, guchar *dst) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; +} + +/* FROM rgba */ + +inline static void +_cogl_rgba_to_g (const guchar *src, guchar *dst) +{ + dst[0] = (src[0] + src[1] + src[2]) / 3; +} + +inline static void +_cogl_rgba_to_rgb (const guchar *src, guchar *dst) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; +} + +inline static void +_cogl_rgba_to_bgr (const guchar *src, guchar *dst) +{ + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; +} + +inline static void +_cogl_rgba_to_bgra (const guchar *src, guchar *dst) +{ + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[3]; +} + +inline static void +_cogl_rgba_to_argb (const guchar *src, guchar *dst) +{ + dst[0] = src[3]; + dst[1] = src[0]; + dst[2] = src[1]; + dst[3] = src[2]; +} + +inline static void +_cogl_rgba_to_abgr (const guchar *src, guchar *dst) +{ + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +/* (Un)Premultiplication */ + +inline static void +_cogl_unpremult_alpha_0 (const guchar *src, guchar *dst) +{ + dst[0] = 0; + dst[1] = 0; + dst[2] = 0; + dst[3] = 0; +} + +inline static void +_cogl_unpremult_alpha_last (const guchar *src, guchar *dst) +{ + guchar alpha = src[3]; + + dst[0] = (((src[0] >> 16) & 0xff) * 255 ) / alpha; + dst[1] = (((src[1] >> 8) & 0xff) * 255 ) / alpha; + dst[2] = (((src[2] >> 0) & 0xff) * 255 ) / alpha; + dst[3] = alpha; +} + +inline static void +_cogl_unpremult_alpha_first (const guchar *src, guchar *dst) +{ + guchar alpha = src[0]; + + dst[0] = alpha; + dst[1] = (((src[1] >> 16) & 0xff) * 255 ) / alpha; + dst[2] = (((src[2] >> 8) & 0xff) * 255 ) / alpha; + dst[3] = (((src[3] >> 0) & 0xff) * 255 ) / alpha; +} + +gboolean +_cogl_bitmap_fallback_can_convert (CoglPixelFormat src, CoglPixelFormat dst) +{ + if (src == dst) + return FALSE; + + switch (src & COGL_UNORDERED_MASK) + { + case COGL_PIXEL_FORMAT_G_8: + case COGL_PIXEL_FORMAT_24: + case COGL_PIXEL_FORMAT_32: + + if ((dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_24 && + (dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_32 && + (dst & COGL_UNORDERED_MASK) != COGL_PIXEL_FORMAT_G_8) + return FALSE; + break; + + default: + return FALSE; + } + + return TRUE; +} + +gboolean +_cogl_bitmap_fallback_can_unpremult (CoglPixelFormat format) +{ + return ((format & COGL_UNORDERED_MASK) == COGL_PIXEL_FORMAT_32); +} + +gboolean +_cogl_bitmap_fallback_convert (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format) +{ + guchar *src; + guchar *dst; + gint src_bpp; + gint dst_bpp; + gint x,y; + guchar temp_rgba[4] = {0,0,0,0}; + + /* Make sure conversion supported */ + if (!_cogl_bitmap_fallback_can_convert (bmp->format, dst_format)) + return FALSE; + + src_bpp = _cogl_get_format_bpp (bmp->format); + dst_bpp = _cogl_get_format_bpp (dst_format); + + /* Initialize destination bitmap */ + *dst_bmp = *bmp; + dst_bmp->rowstride = sizeof(guchar) * dst_bpp * dst_bmp->width; + dst_bmp->format = ((bmp->format & COGL_PREMULT_BIT) | + (dst_format & COGL_UNPREMULT_MASK)); + + /* Allocate a new buffer to hold converted data */ + dst_bmp->data = g_malloc (sizeof(guchar) + * dst_bmp->height + * dst_bmp->rowstride); + + /* FIXME: Optimize */ + for (y = 0; y < bmp->height; y++) + { + src = (guchar*)bmp->data + y * bmp->rowstride; + dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride; + + for (x = 0; x < bmp->width; x++) + { + /* FIXME: Would be nice to at least remove this inner + * branching, but not sure it can be done without + * rewriting of the whole loop */ + switch (bmp->format & COGL_UNPREMULT_MASK) + { + case COGL_PIXEL_FORMAT_G_8: + _cogl_g_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_RGB_888: + _cogl_rgb_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_BGR_888: + _cogl_bgr_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_RGBA_8888: + _cogl_rgba_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_BGRA_8888: + _cogl_bgra_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_ARGB_8888: + _cogl_argb_to_rgba (src, temp_rgba); break; + case COGL_PIXEL_FORMAT_ABGR_8888: + _cogl_abgr_to_rgba (src, temp_rgba); break; + default: + break; + } + + switch (dst_format & COGL_UNPREMULT_MASK) + { + case COGL_PIXEL_FORMAT_G_8: + _cogl_rgba_to_g (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_RGB_888: + _cogl_rgba_to_rgb (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_BGR_888: + _cogl_rgba_to_bgr (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_RGBA_8888: + _cogl_rgba_to_rgba (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_BGRA_8888: + _cogl_rgba_to_bgra (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_ARGB_8888: + _cogl_rgba_to_argb (temp_rgba, dst); break; + case COGL_PIXEL_FORMAT_ABGR_8888: + _cogl_rgba_to_abgr (temp_rgba, dst); break; + default: + break; + } + + src += src_bpp; + dst += dst_bpp; + } + } + + return TRUE; +} + +gboolean +_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp) +{ + guchar *src; + guchar *dst; + gint bpp; + gint x,y; + + /* Make sure format supported for un-premultiplication */ + if (!_cogl_bitmap_fallback_can_unpremult (bmp->format)) + return FALSE; + + bpp = _cogl_get_format_bpp (bmp->format); + + /* Initialize destination bitmap */ + *dst_bmp = *bmp; + dst_bmp->format = (bmp->format & COGL_UNPREMULT_MASK); + + /* Allocate a new buffer to hold converted data */ + dst_bmp->data = g_malloc (sizeof(guchar) + * dst_bmp->height + * dst_bmp->rowstride); + + /* FIXME: Optimize */ + for (y = 0; y < bmp->height; y++) + { + src = (guchar*)bmp->data + y * bmp->rowstride; + dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride; + + for (x = 0; x < bmp->width; x++) + { + /* FIXME: Would be nice to at least remove this inner + * branching, but not sure it can be done without + * rewriting of the whole loop */ + if (bmp->format & COGL_AFIRST_BIT) + { + if (src[0] == 0) + _cogl_unpremult_alpha_0 (src, dst); + else + _cogl_unpremult_alpha_first (src, dst); + } + else + { + if (src[3] == 0) + _cogl_unpremult_alpha_0 (src, dst); + else + _cogl_unpremult_alpha_last (src, dst); + } + + src += bpp; + dst += bpp; + } + } + + return TRUE; +} + +gboolean +_cogl_bitmap_fallback_from_file (CoglBitmap *bmp, + const gchar *filename) +{ + /* FIXME: use jpeglib, libpng, etc. manually maybe */ + return FALSE; +} diff --git a/clutter/cogl/common/cogl-bitmap-pixbuf.c b/clutter/cogl/common/cogl-bitmap-pixbuf.c new file mode 100644 index 000000000..923788607 --- /dev/null +++ b/clutter/cogl/common/cogl-bitmap-pixbuf.c @@ -0,0 +1,197 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-bitmap.h" + +#include + +#ifdef USE_GDKPIXBUF +#include +#endif + +gboolean +_cogl_bitmap_can_convert (CoglPixelFormat src, CoglPixelFormat dst) +{ + return FALSE; +} + +gboolean +_cogl_bitmap_can_unpremult (CoglPixelFormat format) +{ + return FALSE; +} + +gboolean +_cogl_bitmap_convert (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format) +{ + return FALSE; +} + +gboolean +_cogl_bitmap_unpremult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp) +{ + return FALSE; +} + +#ifdef USE_GDKPIXBUF + +gboolean +_cogl_bitmap_from_file (CoglBitmap *bmp, + const gchar *filename, + GError **error) +{ + GdkPixbuf *pixbuf; + gboolean has_alpha; + GdkColorspace color_space; + CoglPixelFormat pixel_format; + gint width; + gint height; + gint rowstride; + gint bits_per_sample; + gint n_channels; + gint last_row_size; + guchar *pixels; + guchar *out_data; + guchar *out; + gint r; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (bmp == NULL) return FALSE; + + /* Load from file using GdkPixbuf */ + pixbuf = gdk_pixbuf_new_from_file (filename, error); + if (pixbuf == NULL) return FALSE; + + /* Get pixbuf properties */ + has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); + color_space = gdk_pixbuf_get_colorspace (pixbuf); + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf); + n_channels = gdk_pixbuf_get_n_channels (pixbuf); + + /* The docs say this is the right way */ + last_row_size = width * ((n_channels * bits_per_sample + 7) / 8); + + /* According to current docs this should be true and so + * the translation to cogl pixel format below valid */ + g_assert (bits_per_sample == 8); + + if (has_alpha) + g_assert (n_channels == 4); + else + g_assert (n_channels == 3); + + /* Translate to cogl pixel format */ + switch (color_space) + { + case GDK_COLORSPACE_RGB: + /* The only format supported by GdkPixbuf so far */ + pixel_format = has_alpha ? + COGL_PIXEL_FORMAT_RGBA_8888 : + COGL_PIXEL_FORMAT_RGB_888; + break; + + default: + /* Ouch, spec changed! */ + g_object_unref (pixbuf); + return FALSE; + } + + /* FIXME: Any way to destroy pixbuf but retain pixel data? */ + + pixels = gdk_pixbuf_get_pixels (pixbuf); + out_data = (guchar*) g_malloc ((height - 1) * rowstride + last_row_size); + out = out_data; + + /* Copy up to last row */ + for (r = 0; r < height-1; ++r) + { + memcpy (out, pixels, rowstride); + pixels += rowstride; + out += rowstride; + } + + /* Copy last row */ + memcpy (out, pixels, last_row_size); + + /* Destroy GdkPixbuf object */ + g_object_unref (pixbuf); + + /* Store bitmap info */ + bmp->data = out_data; + bmp->format = pixel_format; + bmp->width = width; + bmp->height = height; + bmp->rowstride = rowstride; + + return TRUE; +} + +#else + +#include "stb_image.c" + +gboolean +_cogl_bitmap_from_file (CoglBitmap *bmp, + const gchar *filename, + GError **error) +{ + gint stb_pixel_format; + gint width; + gint height; + guchar *pixels; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (bmp == NULL) return FALSE; + + /* Load from file using stb */ + pixels = stbi_load (filename, &width, &height, &stb_pixel_format, STBI_rgb_alpha); + if (pixels == NULL) return FALSE; + + /* Store bitmap info */ + bmp->data = pixels; + bmp->format = COGL_PIXEL_FORMAT_RGBA_8888; + bmp->width = width; + bmp->height = height; + bmp->rowstride = width * 4; + + g_print ("we successfully used stb_image to load %s\n", filename); + + return TRUE; +} +#endif diff --git a/clutter/cogl/common/cogl-bitmap.c b/clutter/cogl/common/cogl-bitmap.c new file mode 100644 index 000000000..39341e0b0 --- /dev/null +++ b/clutter/cogl/common/cogl-bitmap.c @@ -0,0 +1,150 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-bitmap.h" + +#include + +gint +_cogl_get_format_bpp (CoglPixelFormat format) +{ + gint bpp_lut[] = { + 0, /* invalid */ + 1, /* A_8 */ + 3, /* 888 */ + 4, /* 8888 */ + 2, /* 565 */ + 2, /* 4444 */ + 2, /* 5551 */ + 2, /* YUV */ + 1 /* G_8 */ + }; + + return bpp_lut [format & COGL_UNORDERED_MASK]; +} + +gboolean +_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format) +{ + CoglBitmap tmp_bmp = *bmp; + CoglBitmap new_bmp = *bmp; + gboolean new_bmp_owner = FALSE; + + /* Is base format different (not considering premult status)? */ + if ((bmp->format & COGL_UNPREMULT_MASK) != + (dst_format & COGL_UNPREMULT_MASK)) + { + /* Try converting using imaging library */ + if (!_cogl_bitmap_convert (&new_bmp, &tmp_bmp, dst_format)) + { + /* ... or try fallback */ + if (!_cogl_bitmap_fallback_convert (&new_bmp, &tmp_bmp, dst_format)) + return FALSE; + } + + /* Update bitmap with new data */ + new_bmp = tmp_bmp; + new_bmp_owner = TRUE; + } + + /* Do we need to unpremultiply */ + if ((bmp->format & COGL_PREMULT_BIT) == 0 && + (dst_format & COGL_PREMULT_BIT) > 0) + { + /* Try unpremultiplying using imaging library */ + if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp)) + { + /* ... or try fallback */ + if (!_cogl_bitmap_fallback_unpremult (&new_bmp, &tmp_bmp)) + { + if (new_bmp_owner) + g_free (new_bmp.data); + + return FALSE; + } + } + + /* Update bitmap with new data */ + if (new_bmp_owner) + g_free (new_bmp.data); + + new_bmp = tmp_bmp; + new_bmp_owner = TRUE; + } + + /* Do we need to premultiply */ + if ((bmp->format & COGL_PREMULT_BIT) > 0 && + (dst_format & COGL_PREMULT_BIT) == 0) + { + /* FIXME: implement premultiplication */ + if (new_bmp_owner) + g_free (new_bmp.data); + + return FALSE; + } + + /* Output new bitmap info */ + *dst_bmp = new_bmp; + + return TRUE; +} + +void +_cogl_bitmap_copy_subregion (CoglBitmap *src, + CoglBitmap *dst, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height) +{ + guchar *srcdata; + guchar *dstdata; + gint bpp; + gint line; + + /* Intended only for fast copies when format is equal! */ + g_assert (src->format == dst->format); + bpp = _cogl_get_format_bpp (src->format); + + srcdata = src->data + src_y * src->rowstride + src_x * bpp; + dstdata = dst->data + dst_y * dst->rowstride + dst_x * bpp; + + for (line=0; linerowstride; + dstdata += dst->rowstride; + } +} diff --git a/clutter/cogl/common/cogl-bitmap.h b/clutter/cogl/common/cogl-bitmap.h new file mode 100644 index 000000000..ca9f8a9c3 --- /dev/null +++ b/clutter/cogl/common/cogl-bitmap.h @@ -0,0 +1,95 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_BITMAP_H +#define __COGL_BITMAP_H + +#include + +typedef struct _CoglBitmap CoglBitmap; + +struct _CoglBitmap +{ + guchar *data; + CoglPixelFormat format; + gint width; + gint height; + gint rowstride; +}; + +gboolean +_cogl_bitmap_can_convert (CoglPixelFormat src, CoglPixelFormat dst); + +gboolean +_cogl_bitmap_fallback_can_convert (CoglPixelFormat src, CoglPixelFormat dst); + +gboolean +_cogl_bitmap_can_unpremult (CoglPixelFormat format); + +gboolean +_cogl_bitmap_fallback_can_unpremult (CoglPixelFormat format); + +gboolean +_cogl_bitmap_convert (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format); +gboolean +_cogl_bitmap_fallback_convert (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format); + +gboolean +_cogl_bitmap_unpremult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp); + +gboolean +_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp); + +gboolean +_cogl_bitmap_from_file (CoglBitmap *bmp, + const gchar *filename, + GError **error); + +gboolean +_cogl_bitmap_fallback_from_file (CoglBitmap *bmp, + const gchar *filename); + +gboolean +_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format); + +void +_cogl_bitmap_copy_subregion (CoglBitmap *src, + CoglBitmap *dst, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height); + +#endif /* __COGL_BITMAP_H */ diff --git a/clutter/cogl/common/cogl-util.c b/clutter/cogl/common/cogl-util.c new file mode 100644 index 000000000..b024e763e --- /dev/null +++ b/clutter/cogl/common/cogl-util.c @@ -0,0 +1,51 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" + +/** + * cogl_util_next_p2: + * @a: Value to get the next power + * + * Calculates the next power greater than @a. + * + * Return value: The next power after @a. + */ +int +cogl_util_next_p2 (int a) +{ + int rval=1; + + while(rval < a) + rval <<= 1; + + return rval; +} diff --git a/clutter/cogl/common/cogl-util.h b/clutter/cogl/common/cogl-util.h new file mode 100644 index 000000000..759da8cc7 --- /dev/null +++ b/clutter/cogl/common/cogl-util.h @@ -0,0 +1,32 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_UTIL_H +#define __COGL_UTIL_H + +int +cogl_util_next_p2 (int a); + +#endif /* __COGL_UTIL_H */ diff --git a/clutter/cogl/common/stb_image.c b/clutter/cogl/common/stb_image.c new file mode 100644 index 000000000..bd3e63b35 --- /dev/null +++ b/clutter/cogl/common/stb_image.c @@ -0,0 +1,3772 @@ +/* stbi-1.12 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c + when you control the images you're loading + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline (no JPEG progressive, no oddball channel decimations) + PNG non-interlaced + BMP non-1bpp, non-RLE + TGA (not sure what subset, if a subset) + PSD (composited view only, no extra channels) + HDR (radiance rgbE format) + writes BMP,TGA (define STBI_NO_WRITE to remove code) + decoded from memory or through stdio FILE (define STBI_NO_STDIO to remove code) + supports installable dequantizing-IDCT, YCbCr-to-RGB conversion (define STBI_SIMD) + + TODO: + stbi_info_* + + history: + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi_bmp_load() and stbi_tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less + than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant +*/ + + +//// begin header file //////////////////////////////////////////////////// +// +// Limitations: +// - no progressive/interlaced support (jpeg, png) +// - 8-bit samples only (jpeg, png) +// - not threadsafe +// - channel subsampling of at most 2 in each dimension (jpeg) +// - no delayed line count (jpeg) -- IJG doesn't support either +// +// Basic usage (see HDR discussion below): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to easily see if it's opaque. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG and BMP images are automatically depalettized. +// +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); + + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_NO_HDR +#include // ldexp +#include // strcmp +#endif + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4, +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +// WRITING API + +#if !defined(STBI_NO_WRITE) && !defined(STBI_NO_STDIO) +// write a BMP/TGA file given tightly packed 'comp' channels (no padding, nor bmp-stride-padding) +// (you must include the appropriate extension in the filename). +// returns TRUE on success, FALSE if couldn't open file, error writing file +extern int stbi_write_bmp (char const *filename, int x, int y, int comp, void *data); +extern int stbi_write_tga (char const *filename, int x, int y, int comp, void *data); +#endif + +// PRIMARY API - works on images of any type + +// load image by filename, open file, or memory buffer +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image + +#ifndef STBI_NO_HDR +#ifndef STBI_NO_STDIO +extern float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif +extern float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + +extern void stbi_hdr_to_ldr_gamma(float gamma); +extern void stbi_hdr_to_ldr_scale(float scale); + +extern void stbi_ldr_to_hdr_gamma(float gamma); +extern void stbi_ldr_to_hdr_scale(float scale); + +#endif // STBI_NO_HDR + +// get a VERY brief reason for failure +extern char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +extern void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_is_hdr (char const *filename); +extern int stbi_is_hdr_from_file(FILE *f); +#endif + +// ZLIB client - used by PNG, available for other purposes + +extern char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen); +extern char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +extern char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +extern int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +// TYPE-SPECIFIC ACCESS + +// is it a jpeg? +extern int stbi_jpeg_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_jpeg_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_jpeg_test_file (FILE *f); +extern stbi_uc *stbi_jpeg_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +extern int stbi_jpeg_dc_only; // only decode DC component + +// is it a png? +extern int stbi_png_test_memory (stbi_uc const *buffer, int len); +extern stbi_uc *stbi_png_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +extern stbi_uc *stbi_png_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_png_test_file (FILE *f); +extern stbi_uc *stbi_png_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif + +// is it a bmp? +extern int stbi_bmp_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_bmp_test_file (FILE *f); +extern stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a tga? +extern int stbi_tga_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_tga_test_file (FILE *f); +extern stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it a psd? +extern int stbi_psd_test_memory (stbi_uc const *buffer, int len); + +extern stbi_uc *stbi_psd_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_psd_test_file (FILE *f); +extern stbi_uc *stbi_psd_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// is it an hdr? +extern int stbi_hdr_test_memory (stbi_uc const *buffer, int len); + +extern float * stbi_hdr_load (char const *filename, int *x, int *y, int *comp, int req_comp); +extern float * stbi_hdr_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); +#ifndef STBI_NO_STDIO +extern int stbi_hdr_test_file (FILE *f); +extern float * stbi_hdr_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +#endif + +// define new loaders +typedef struct +{ + int (*test_memory)(stbi_uc const *buffer, int len); + stbi_uc * (*load_from_memory)(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + #ifndef STBI_NO_STDIO + int (*test_file)(FILE *f); + stbi_uc * (*load_from_file)(FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +} stbi_loader; + +// register a loader by filling out the above structure (you must defined ALL functions) +// returns 1 if added or already added, 0 if not added (too many loaders) +extern int stbi_register_loader(stbi_loader *loader); + +// define faster low-level operations (typically SIMD support) +#if STBI_SIMD +typedef void (*stbi_idct_8x8)(uint8 *out, int out_stride, short data[64], unsigned short *dequantize); +// compute an integer IDCT on "input" +// input[x] = data[x] * dequantize[x] +// write results to 'out': 64 samples, each run of 8 spaced by 'out_stride' +// CLAMP results to 0..255 +typedef void (*stbi_YCbCr_to_RGB_run)(uint8 *output, uint8 const *y, uint8 const *cb, uint8 const *cr, int count, int step); +// compute a conversion from YCbCr to RGB +// 'count' pixels +// write pixels to 'output'; each pixel is 'step' bytes (either 3 or 4; if 4, write '255' as 4th), order R,G,B +// y: Y input channel +// cb: Cb input channel; scale/biased to be 0..255 +// cr: Cr input channel; scale/biased to be 0..255 + +extern void stbi_install_idct(stbi_idct_8x8 func); +extern void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func); +#endif // STBI_SIMD + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// + +#ifndef STBI_NO_STDIO +#include +#endif +#include +#include +#include +#include + +#if STBI_SIMD +#include +#endif + +#ifndef _MSC_VER +#define __forceinline +#endif + + +// implementation: +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +typedef unsigned int uint; + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(uint32)==4]; + +#if defined(STBI_NO_STDIO) && !defined(STBI_NO_WRITE) +#define STBI_NO_WRITE +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// Generic API that works on all image types +// + +static char *failure_reason; + +char *stbi_failure_reason(void) +{ + return failure_reason; +} + +static int e(char *str) +{ + failure_reason = str; + return 0; +} + +#ifdef STBI_NO_FAILURE_STRINGS + #define e(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define e(x,y) e(y) +#else + #define e(x,y) e(x) +#endif + +#define epf(x,y) ((float *) (e(x,y)?NULL:NULL)) +#define epuc(x,y) ((unsigned char *) (e(x,y)?NULL:NULL)) + +void stbi_image_free(void *retval_from_stbi_load) +{ + free(retval_from_stbi_load); +} + +#define MAX_LOADERS 32 +stbi_loader *loaders[MAX_LOADERS]; +static int max_loaders = 0; + +int stbi_register_loader(stbi_loader *loader) +{ + int i; + for (i=0; i < MAX_LOADERS; ++i) { + // already present? + if (loaders[i] == loader) + return 1; + // end of the list? + if (loaders[i] == NULL) { + loaders[i] = loader; + max_loaders = i+1; + return 1; + } + } + // no room for it + return 0; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_STDIO +unsigned char *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + unsigned char *result; + if (!f) return epuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + int i; + if (stbi_jpeg_test_file(f)) + return stbi_jpeg_load_from_file(f,x,y,comp,req_comp); + if (stbi_png_test_file(f)) + return stbi_png_load_from_file(f,x,y,comp,req_comp); + if (stbi_bmp_test_file(f)) + return stbi_bmp_load_from_file(f,x,y,comp,req_comp); + if (stbi_psd_test_file(f)) + return stbi_psd_load_from_file(f,x,y,comp,req_comp); + #ifndef STBI_NO_HDR + if (stbi_hdr_test_file(f)) { + float *hdr = stbi_hdr_load_from_file(f, x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + for (i=0; i < max_loaders; ++i) + if (loaders[i]->test_file(f)) + return loaders[i]->load_from_file(f,x,y,comp,req_comp); + // test tga last because it's a crappy test! + if (stbi_tga_test_file(f)) + return stbi_tga_load_from_file(f,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +unsigned char *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + int i; + if (stbi_jpeg_test_memory(buffer,len)) + return stbi_jpeg_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_png_test_memory(buffer,len)) + return stbi_png_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_bmp_test_memory(buffer,len)) + return stbi_bmp_load_from_memory(buffer,len,x,y,comp,req_comp); + if (stbi_psd_test_memory(buffer,len)) + return stbi_psd_load_from_memory(buffer,len,x,y,comp,req_comp); + #ifndef STBI_NO_HDR + if (stbi_hdr_test_memory(buffer, len)) { + float *hdr = stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + return hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + for (i=0; i < max_loaders; ++i) + if (loaders[i]->test_memory(buffer,len)) + return loaders[i]->load_from_memory(buffer,len,x,y,comp,req_comp); + // test tga last because it's a crappy test! + if (stbi_tga_test_memory(buffer,len)) + return stbi_tga_load_from_memory(buffer,len,x,y,comp,req_comp); + return epuc("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_HDR + +#ifndef STBI_NO_STDIO +float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = fopen(filename, "rb"); + float *result; + if (!f) return epf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test_file(f)) + return stbi_hdr_load_from_file(f,x,y,comp,req_comp); + #endif + data = stbi_load_from_file(f, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + #ifndef STBI_NO_HDR + if (stbi_hdr_test_memory(buffer, len)) + return stbi_hdr_load_from_memory(buffer, len,x,y,comp,req_comp); + #endif + data = stbi_load_from_memory(buffer, len, x, y, comp, req_comp); + if (data) + return ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return epf("unknown image type", "Image not of any known type, or corrupt"); +} +#endif + +// these is-hdr-or-not is defined independent of whether STBI_NO_HDR is +// defined, for API simplicity; if STBI_NO_HDR is defined, it always +// reports false! + +extern int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + return stbi_hdr_test_memory(buffer, len); + #else + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +extern int stbi_is_hdr (char const *filename) +{ + FILE *f = fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +extern int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + return stbi_hdr_test_file(f); + #else + return 0; + #endif +} + +#endif + +// @TODO: get image dimensions & components without fully decoding +#ifndef STBI_NO_STDIO +extern int stbi_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +#ifndef STBI_NO_HDR +static float h2l_gamma_i=1.0f/2.2f, h2l_scale_i=1.0f; +static float l2h_gamma=2.2f, l2h_scale=1.0f; + +void stbi_hdr_to_ldr_gamma(float gamma) { h2l_gamma_i = 1/gamma; } +void stbi_hdr_to_ldr_scale(float scale) { h2l_scale_i = 1/scale; } + +void stbi_ldr_to_hdr_gamma(float gamma) { l2h_gamma = gamma; } +void stbi_ldr_to_hdr_scale(float scale) { l2h_scale = scale; } +#endif + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +// image width, height, # components +static uint32 img_x, img_y; +static int img_n, img_out_n; + +enum +{ + SCAN_load=0, + SCAN_type, + SCAN_header, +}; + +// An API for reading either from memory or file. +#ifndef STBI_NO_STDIO +static FILE *img_file; +#endif +static uint8 const *img_buffer, *img_buffer_end; + +#ifndef STBI_NO_STDIO +static void start_file(FILE *f) +{ + img_file = f; +} +#endif + +static void start_mem(uint8 const *buffer, int len) +{ +#ifndef STBI_NO_STDIO + img_file = NULL; +#endif + img_buffer = buffer; + img_buffer_end = buffer+len; +} + +static int get8(void) +{ +#ifndef STBI_NO_STDIO + if (img_file) { + int c = fgetc(img_file); + return c == EOF ? 0 : c; + } +#endif + if (img_buffer < img_buffer_end) + return *img_buffer++; + return 0; +} + +static int at_eof(void) +{ +#ifndef STBI_NO_STDIO + if (img_file) + return feof(img_file); +#endif + return img_buffer >= img_buffer_end; +} + +static uint8 get8u(void) +{ + return (uint8) get8(); +} + +static void skip(int n) +{ +#ifndef STBI_NO_STDIO + if (img_file) + fseek(img_file, n, SEEK_CUR); + else +#endif + img_buffer += n; +} + +static int get16(void) +{ + int z = get8(); + return (z << 8) + get8(); +} + +static uint32 get32(void) +{ + uint32 z = get16(); + return (z << 16) + get16(); +} + +static int get16le(void) +{ + int z = get8(); + return z + (get8() << 8); +} + +static uint32 get32le(void) +{ + uint32 z = get16le(); + return z + (get16le() << 16); +} + +static void getn(stbi_uc *buffer, int n) +{ +#ifndef STBI_NO_STDIO + if (img_file) { + fread(buffer, 1, n, img_file); + return; + } +#endif + memcpy(buffer, img_buffer, n); + img_buffer += n; +} + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static uint8 compute_y(int r, int g, int b) +{ + return (uint8) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *convert_format(unsigned char *data, int img_n, int req_comp) +{ + uint i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * img_x * img_y); + if (good == NULL) { + free(data); + return epuc("outofmem", "Out of memory"); + } + + for (j=0; j < img_y; ++j) { + unsigned char *src = data + j * img_x * img_n ; + unsigned char *dest = good + j * img_x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=0; i < img_x; ++i, src += a, dest += b) + + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch(COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: assert(0); + } + #undef CASE + } + + free(data); + img_out_n = req_comp; + return good; +} + +#ifndef STBI_NO_HDR +static float *ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) malloc(x * y * comp * sizeof(float)); + if (output == NULL) { free(data); return epf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) pow(data[i*comp+k]/255.0f, l2h_gamma) * l2h_scale; + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + free(data); + return output; +} + +#define float2int(x) ((int) (x)) +static stbi_uc *hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) malloc(x * y * comp); + if (output == NULL) { free(data); return epuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*h2l_scale_i, h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = float2int(z); + } + } + free(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder (not actually fully baseline implementation) +// +// simple implementation +// - channel subsampling of at most 2 in each dimension +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - uses a lot of intermediate memory, could cache poorly +// - load http://nothings.org/remote/anemones.jpg 3 times on 2.8Ghz P4 +// stb_jpeg: 1.34 seconds (MSVC6, default release build) +// stb_jpeg: 1.06 seconds (MSVC6, processor = Pentium Pro) +// IJL11.dll: 1.08 seconds (compiled by intel) +// IJG 1998: 0.98 seconds (MSVC6, makefile provided by IJG) +// IJG 1998: 0.95 seconds (MSVC6, makefile + proc=PPro) + +int stbi_jpeg_dc_only; + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + uint8 fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + uint16 code[256]; + uint8 values[256]; + uint8 size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} huffman; + +static huffman huff_dc[4]; // baseline is 2 tables, extended is 4 +static huffman huff_ac[4]; +static uint8 dequant[4][64]; +#if STBI_SIMD +static __declspec(align(16)) unsigned short dequant2[4][64]; +#endif + +static int build_huffman(huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (uint8) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (uint16) (code++); + if (code-1 >= (1 << j)) return e("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (uint8) i; + } + } + } + return 1; +} + +// sizes for components, interleaved MCUs +static int img_h_max, img_v_max; +static int img_mcu_x, img_mcu_y; +static int img_mcu_w, img_mcu_h; + +// definition of jpeg image component +static struct +{ + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + uint8 *data; + void *raw_data; + uint8 *linebuf; +} img_comp[4]; + +static uint32 code_buffer; // jpeg entropy-coded buffer +static int code_bits; // number of valid bits +static unsigned char marker; // marker seen while filling entropy buffer +static int nomore; // flag if we saw a marker so must stop + +static void grow_buffer_unsafe(void) +{ + do { + int b = nomore ? 0 : get8(); + if (b == 0xff) { + int c = get8(); + if (c != 0) { + marker = (unsigned char) c; + nomore = 1; + return; + } + } + code_buffer = (code_buffer << 8) | b; + code_bits += 8; + } while (code_bits <= 24); +} + +// (1 << n) - 1 +static uint32 bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +__forceinline static int decode(huffman *h) +{ + unsigned int temp; + int c,k; + + if (code_bits < 16) grow_buffer_unsafe(); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (code_buffer >> (code_bits - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + if (h->size[k] > code_bits) + return -1; + code_bits -= h->size[k]; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + if (code_bits < 16) + temp = (code_buffer << (16 - code_bits)) & 0xffff; + else + temp = (code_buffer >> (code_bits - 16)) & 0xffff; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + code_bits -= 16; + return -1; + } + + if (k > code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((code_buffer >> (code_bits - k)) & bmask[k]) + h->delta[k]; + assert((((code_buffer) >> (code_bits - h->size[c])) & bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + code_bits -= k; + return h->values[c]; +} + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +__forceinline static int extend_receive(int n) +{ + unsigned int m = 1 << (n-1); + unsigned int k; + if (code_bits < n) grow_buffer_unsafe(); + k = (code_buffer >> (code_bits - n)) & bmask[n]; + code_bits -= n; + // the following test is probably a random branch that won't + // predict well. I tried to table accelerate it but failed. + // maybe it's compiling as a conditional move? + if (k < m) + return (-1 << n) + k + 1; + else + return k; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static uint8 dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int decode_block(short data[64], huffman *hdc, huffman *hac, int b) +{ + int diff,dc,k; + int t = decode(hdc); + if (t < 0) return e("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? extend_receive(t) : 0; + dc = img_comp[b].dc_pred + diff; + img_comp[b].dc_pred = dc; + data[0] = (short) dc; + + // decode AC components, see JPEG spec + k = 1; + do { + int r,s; + int rs = decode(hac); + if (rs < 0) return e("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + data[dezigzag[k++]] = (short) extend_receive(s); + } + } while (k < 64); + return 1; +} + +// take a -128..127 value and clamp it and convert to 0..255 +__forceinline static uint8 clamp(int x) +{ + x += 128; + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (uint8) x; +} + +#define f2f(x) (int) (((x) * 4096 + 0.5)) +#define fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * f2f(0.5411961f); \ + t2 = p1 + p3*f2f(-1.847759065f); \ + t3 = p1 + p2*f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = fsh(p2+p3); \ + t1 = fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*f2f( 1.175875602f); \ + t0 = t0*f2f( 0.298631336f); \ + t1 = t1*f2f( 2.053119869f); \ + t2 = t2*f2f( 3.072711026f); \ + t3 = t3*f2f( 1.501321110f); \ + p1 = p5 + p1*f2f(-0.899976223f); \ + p2 = p5 + p2*f2f(-2.562915447f); \ + p3 = p3*f2f(-1.961570560f); \ + p4 = p4*f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +#if !STBI_SIMD +// .344 seconds on 3*anemones.jpg +static void idct_block(uint8 *out, int out_stride, short data[64], uint8 *dequantize) +{ + int i,val[64],*v=val; + uint8 *o,*dq = dequantize; + short *d = data; + + if (stbi_jpeg_dc_only) { + // ok, I don't really know why this is right, but it seems to be: + int z = 128 + ((d[0] * dq[0]) >> 3); + for (i=0; i < 8; ++i) { + out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z; + out += out_stride; + } + return; + } + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} +#else +static void idct_block(uint8 *out, int out_stride, short data[64], unsigned short *dequantize) +{ + int i,val[64],*v=val; + uint8 *o; + unsigned short *dq = dequantize; + short *d = data; + + if (stbi_jpeg_dc_only) { + // ok, I don't really know why this is right, but it seems to be: + int z = 128 + ((d[0] * dq[0]) >> 3); + for (i=0; i < 8; ++i) { + out[0] = out[1] = out[2] = out[3] = out[4] = out[5] = out[6] = out[7] = z; + out += out_stride; + } + return; + } + + // columns + for (i=0; i < 8; ++i,++d,++dq, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * dq[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + IDCT_1D(d[ 0]*dq[ 0],d[ 8]*dq[ 8],d[16]*dq[16],d[24]*dq[24], + d[32]*dq[32],d[40]*dq[40],d[48]*dq[48],d[56]*dq[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + x0 += 65536; x1 += 65536; x2 += 65536; x3 += 65536; + o[0] = clamp((x0+t3) >> 17); + o[7] = clamp((x0-t3) >> 17); + o[1] = clamp((x1+t2) >> 17); + o[6] = clamp((x1-t2) >> 17); + o[2] = clamp((x2+t1) >> 17); + o[5] = clamp((x2-t1) >> 17); + o[3] = clamp((x3+t0) >> 17); + o[4] = clamp((x3-t0) >> 17); + } +} +static stbi_idct_8x8 stbi_idct_installed = idct_block; + +extern void stbi_install_idct(stbi_idct_8x8 func) +{ + stbi_idct_installed = func; +} +#endif + +#define MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static uint8 get_marker(void) +{ + uint8 x; + if (marker != MARKER_none) { x = marker; marker = MARKER_none; return x; } + x = get8u(); + if (x != 0xff) return MARKER_none; + while (x == 0xff) + x = get8u(); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +static int scan_n, order[4]; +static int restart_interval, todo; +#define RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, reset the entropy decoder and +// the dc prediction +static void reset(void) +{ + code_bits = 0; + code_buffer = 0; + nomore = 0; + img_comp[0].dc_pred = img_comp[1].dc_pred = img_comp[2].dc_pred = 0; + marker = MARKER_none; + todo = restart_interval ? restart_interval : 0x7fffffff; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int parse_entropy_coded_data(void) +{ + reset(); + if (scan_n == 1) { + int i,j; + #if STBI_SIMD + __declspec(align(16)) + #endif + short data[64]; + int n = order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (img_comp[n].x+7) >> 3; + int h = (img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant2[img_comp[n].tq]); + #else + idct_block(img_comp[n].data+img_comp[n].w2*j*8+i*8, img_comp[n].w2, data, dequant[img_comp[n].tq]); + #endif + // every data block is an MCU, so countdown the restart interval + if (--todo <= 0) { + if (code_bits < 24) grow_buffer_unsafe(); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(marker)) return 1; + reset(); + } + } + } + } else { // interleaved! + int i,j,k,x,y; + short data[64]; + for (j=0; j < img_mcu_y; ++j) { + for (i=0; i < img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < scan_n; ++k) { + int n = order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < img_comp[n].v; ++y) { + for (x=0; x < img_comp[n].h; ++x) { + int x2 = (i*img_comp[n].h + x)*8; + int y2 = (j*img_comp[n].v + y)*8; + if (!decode_block(data, huff_dc+img_comp[n].hd, huff_ac+img_comp[n].ha, n)) return 0; + #if STBI_SIMD + stbi_idct_installed(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant2[img_comp[n].tq]); + #else + idct_block(img_comp[n].data+img_comp[n].w2*y2+x2, img_comp[n].w2, data, dequant[img_comp[n].tq]); + #endif + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--todo <= 0) { + if (code_bits < 24) grow_buffer_unsafe(); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!RESTART(marker)) return 1; + reset(); + } + } + } + } + return 1; +} + +static int process_marker(int m) +{ + int L; + switch (m) { + case MARKER_none: // no marker found + return e("expected marker","Corrupt JPEG"); + + case 0xC2: // SOF - progressive + return e("progressive jpeg","JPEG format not supported (progressive)"); + + case 0xDD: // DRI - specify restart interval + if (get16() != 4) return e("bad DRI len","Corrupt JPEG"); + restart_interval = get16(); + return 1; + + case 0xDB: // DQT - define quantization table + L = get16()-2; + while (L > 0) { + int z = get8(); + int p = z >> 4; + int t = z & 15,i; + if (p != 0) return e("bad DQT type","Corrupt JPEG"); + if (t > 3) return e("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + dequant[t][dezigzag[i]] = get8u(); + #if STBI_SIMD + for (i=0; i < 64; ++i) + dequant2[t][i] = dequant[t][i]; + #endif + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = get16()-2; + while (L > 0) { + uint8 *v; + int sizes[16],i,m=0; + int z = get8(); + int tc = z >> 4; + int th = z & 15; + if (tc > 1 || th > 3) return e("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = get8(); + m += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!build_huffman(huff_dc+th, sizes)) return 0; + v = huff_dc[th].values; + } else { + if (!build_huffman(huff_ac+th, sizes)) return 0; + v = huff_ac[th].values; + } + for (i=0; i < m; ++i) + v[i] = get8u(); + L -= m; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + skip(get16()-2); + return 1; + } + return 0; +} + +// after we see SOS +static int process_scan_header(void) +{ + int i; + int Ls = get16(); + scan_n = get8(); + if (scan_n < 1 || scan_n > 4 || scan_n > (int) img_n) return e("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*scan_n) return e("bad SOS len","Corrupt JPEG"); + for (i=0; i < scan_n; ++i) { + int id = get8(), which; + int z = get8(); + for (which = 0; which < img_n; ++which) + if (img_comp[which].id == id) + break; + if (which == img_n) return 0; + img_comp[which].hd = z >> 4; if (img_comp[which].hd > 3) return e("bad DC huff","Corrupt JPEG"); + img_comp[which].ha = z & 15; if (img_comp[which].ha > 3) return e("bad AC huff","Corrupt JPEG"); + order[i] = which; + } + if (get8() != 0) return e("bad SOS","Corrupt JPEG"); + get8(); // should be 63, but might be 0 + if (get8() != 0) return e("bad SOS","Corrupt JPEG"); + + return 1; +} + +static int process_frame_header(int scan) +{ + int Lf,p,i,z, h_max=1,v_max=1; + Lf = get16(); if (Lf < 11) return e("bad SOF len","Corrupt JPEG"); // JPEG + p = get8(); if (p != 8) return e("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + img_y = get16(); if (img_y == 0) return e("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + img_x = get16(); if (img_x == 0) return e("0 width","Corrupt JPEG"); // JPEG requires + img_n = get8(); + if (img_n != 3 && img_n != 1) return e("bad component count","Corrupt JPEG"); // JFIF requires + + if (Lf != 8+3*img_n) return e("bad SOF len","Corrupt JPEG"); + + for (i=0; i < img_n; ++i) { + img_comp[i].id = get8(); + if (img_comp[i].id != i+1) // JFIF requires + if (img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files! + return e("bad component ID","Corrupt JPEG"); + z = get8(); + img_comp[i].h = (z >> 4); if (!img_comp[i].h || img_comp[i].h > 4) return e("bad H","Corrupt JPEG"); + img_comp[i].v = z & 15; if (!img_comp[i].v || img_comp[i].v > 4) return e("bad V","Corrupt JPEG"); + img_comp[i].tq = get8(); if (img_comp[i].tq > 3) return e("bad TQ","Corrupt JPEG"); + } + + if (scan != SCAN_load) return 1; + + if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode"); + + for (i=0; i < img_n; ++i) { + if (img_comp[i].h > h_max) h_max = img_comp[i].h; + if (img_comp[i].v > v_max) v_max = img_comp[i].v; + } + + // compute interleaved mcu info + img_h_max = h_max; + img_v_max = v_max; + img_mcu_w = h_max * 8; + img_mcu_h = v_max * 8; + img_mcu_x = (img_x + img_mcu_w-1) / img_mcu_w; + img_mcu_y = (img_y + img_mcu_h-1) / img_mcu_h; + + for (i=0; i < img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + img_comp[i].x = (img_x * img_comp[i].h + h_max-1) / h_max; + img_comp[i].y = (img_y * img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + img_comp[i].w2 = img_mcu_x * img_comp[i].h * 8; + img_comp[i].h2 = img_mcu_y * img_comp[i].v * 8; + img_comp[i].raw_data = malloc(img_comp[i].w2 * img_comp[i].h2+15); + if (img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + free(img_comp[i].raw_data); + img_comp[i].data = NULL; + } + return e("outofmem", "Out of memory"); + } + img_comp[i].data = (uint8*) (((int) img_comp[i].raw_data + 15) & ~15); + img_comp[i].linebuf = NULL; + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define DNL(x) ((x) == 0xdc) +#define SOI(x) ((x) == 0xd8) +#define EOI(x) ((x) == 0xd9) +#define SOF(x) ((x) == 0xc0 || (x) == 0xc1) +#define SOS(x) ((x) == 0xda) + +static int decode_jpeg_header(int scan) +{ + int m; + marker = MARKER_none; // initialize cached marker to empty + m = get_marker(); + if (!SOI(m)) return e("no SOI","Corrupt JPEG"); + if (scan == SCAN_type) return 1; + m = get_marker(); + while (!SOF(m)) { + if (!process_marker(m)) return 0; + m = get_marker(); + while (m == MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (at_eof()) return e("no SOF", "Corrupt JPEG"); + m = get_marker(); + } + } + if (!process_frame_header(scan)) return 0; + return 1; +} + +static int decode_jpeg_image(void) +{ + int m; + restart_interval = 0; + if (!decode_jpeg_header(SCAN_load)) return 0; + m = get_marker(); + while (!EOI(m)) { + if (SOS(m)) { + if (!process_scan_header()) return 0; + if (!parse_entropy_coded_data()) return 0; + } else { + if (!process_marker(m)) return 0; + } + m = get_marker(); + } + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef uint8 *(*resample_row_func)(uint8 *out, uint8 *in0, uint8 *in1, + int w, int hs); + +#define div4(x) ((uint8) ((x) >> 2)) + +static uint8 *resample_row_1(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + return in_near; +} + +static uint8* resample_row_v_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + for (i=0; i < w; ++i) + out[i] = div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static uint8* resample_row_h_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + uint8 *input = in_near; + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = div4(n+input[i-1]); + out[i*2+1] = div4(n+input[i+1]); + } + out[i*2+0] = div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + return out; +} + +#define div16(x) ((uint8) ((x) >> 4)) + +static uint8 *resample_row_hv_2(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = div16(3*t0 + t1 + 8); + out[i*2 ] = div16(3*t1 + t0 + 8); + } + out[w*2-1] = div4(t1+2); + return out; +} + +static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) + +// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro) +// VC6 without processor=Pro is generating multiple LEAs per multiply! +static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (uint8)r; + out[1] = (uint8)g; + out[2] = (uint8)b; + out[3] = 255; + out += step; + } +} + +#if STBI_SIMD +static stbi_YCbCr_to_RGB_run stbi_YCbCr_installed = YCbCr_to_RGB_row; + +void stbi_install_YCbCr_to_RGB(stbi_YCbCr_to_RGB_run func) +{ + stbi_YCbCr_installed = func; +} +#endif + + +// clean up the temporary component buffers +static void cleanup_jpeg(void) +{ + int i; + for (i=0; i < img_n; ++i) { + if (img_comp[i].data) { + free(img_comp[i].raw_data); + img_comp[i].data = NULL; + } + if (img_comp[i].linebuf) { + free(img_comp[i].linebuf); + img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + uint8 *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi_resample; + +static uint8 *load_jpeg_image(int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + // validate req_comp + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source + if (!decode_jpeg_image()) { cleanup_jpeg(); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : img_n; + + if (img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = img_n; + + // resample and color-convert + { + int k; + uint i,j; + uint8 *output; + uint8 *coutput[4]; + + stbi_resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + img_comp[k].linebuf = (uint8 *) malloc(img_x + 3); + if (!img_comp[k].linebuf) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); } + + r->hs = img_h_max / img_comp[k].h; + r->vs = img_v_max / img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = resample_row_hv_2; + else r->resample = resample_row_generic; + } + + // can't error after this so, this is safe + output = (uint8 *) malloc(n * img_x * img_y + 1); + if (!output) { cleanup_jpeg(); return epuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < img_y; ++j) { + uint8 *out = output + n * img_x * j; + for (k=0; k < decode_n; ++k) { + stbi_resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < img_comp[k].y) + r->line1 += img_comp[k].w2; + } + } + if (n >= 3) { + uint8 *y = coutput[0]; + if (img_n == 3) { + #if STBI_SIMD + stbi_YCbCr_installed(out, y, coutput[1], coutput[2], img_x, n); + #else + YCbCr_to_RGB_row(out, y, coutput[1], coutput[2], img_x, n); + #endif + } else + for (i=0; i < img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + uint8 *y = coutput[0]; + if (n == 1) + for (i=0; i < img_x; ++i) out[i] = y[i]; + else + for (i=0; i < img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + cleanup_jpeg(); + *out_x = img_x; + *out_y = img_y; + if (comp) *comp = img_n; // report original components, not output + return output; + } +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_jpeg_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return load_jpeg_image(x,y,comp,req_comp); +} + +unsigned char *stbi_jpeg_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_jpeg_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +unsigned char *stbi_jpeg_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer,len); + return load_jpeg_image(x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +int stbi_jpeg_test_file(FILE *f) +{ + int n,r; + n = ftell(f); + start_file(f); + r = decode_jpeg_header(SCAN_type); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_jpeg_test_memory(stbi_uc const *buffer, int len) +{ + start_mem(buffer,len); + return decode_jpeg_header(SCAN_type); +} + +// @TODO: +#ifndef STBI_NO_STDIO +extern int stbi_jpeg_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_jpeg_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_jpeg_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define ZFAST_BITS 9 // accelerate all cases in default tables +#define ZFAST_MASK ((1 << ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + uint16 fast[1 << ZFAST_BITS]; + uint16 firstcode[16]; + int maxcode[17]; + uint16 firstsymbol[16]; + uint8 size[288]; + uint16 value[288]; +} zhuffman; + +__forceinline static int bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +__forceinline static int bit_reverse(int v, int bits) +{ + assert(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return bitreverse16(v) >> (16-bits); +} + +static int zbuild_huffman(zhuffman *z, uint8 *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 255, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + assert(sizes[i] <= (1 << i)); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (uint16) code; + z->firstsymbol[i] = (uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return e("bad codelengths","Corrupt JPEG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + z->size[c] = (uint8)s; + z->value[c] = (uint16)i; + if (s <= ZFAST_BITS) { + int k = bit_reverse(next_code[s],s); + while (k < (1 << ZFAST_BITS)) { + z->fast[k] = (uint16) c; + k += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +static uint8 *zbuffer, *zbuffer_end; + +__forceinline static int zget8(void) +{ + if (zbuffer >= zbuffer_end) return 0; + return *zbuffer++; +} + +//static uint32 code_buffer; +static int num_bits; + +static void fill_bits(void) +{ + do { + assert(code_buffer < (1U << num_bits)); + code_buffer |= zget8() << num_bits; + num_bits += 8; + } while (num_bits <= 24); +} + +__forceinline static unsigned int zreceive(int n) +{ + unsigned int k; + if (num_bits < n) fill_bits(); + k = code_buffer & ((1 << n) - 1); + code_buffer >>= n; + num_bits -= n; + return k; +} + +__forceinline static int zhuffman_decode(zhuffman *z) +{ + int b,s,k; + if (num_bits < 16) fill_bits(); + b = z->fast[code_buffer & ZFAST_MASK]; + if (b < 0xffff) { + s = z->size[b]; + code_buffer >>= s; + num_bits -= s; + return z->value[b]; + } + + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = bit_reverse(code_buffer, 16); + for (s=ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + assert(z->size[b] == s); + code_buffer >>= s; + num_bits -= s; + return z->value[b]; +} + +static char *zout; +static char *zout_start; +static char *zout_end; +static int z_expandable; + +static int expand(int n) // need to make room for n bytes +{ + char *q; + int cur, limit; + if (!z_expandable) return e("output buffer limit","Corrupt PNG"); + cur = (int) (zout - zout_start); + limit = (int) (zout_end - zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) realloc(zout_start, limit); + if (q == NULL) return e("outofmem", "Out of memory"); + zout_start = q; + zout = q + cur; + zout_end = q + limit; + return 1; +} + +static zhuffman z_length, z_distance; + +static int length_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int length_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int dist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int parse_huffman_block(void) +{ + for(;;) { + int z = zhuffman_decode(&z_length); + if (z < 256) { + if (z < 0) return e("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= zout_end) if (!expand(1)) return 0; + *zout++ = (char) z; + } else { + uint8 *p; + int len,dist; + if (z == 256) return 1; + z -= 257; + len = length_base[z]; + if (length_extra[z]) len += zreceive(length_extra[z]); + z = zhuffman_decode(&z_distance); + if (z < 0) return e("bad huffman code","Corrupt PNG"); + dist = dist_base[z]; + if (dist_extra[z]) dist += zreceive(dist_extra[z]); + if (zout - zout_start < dist) return e("bad dist","Corrupt PNG"); + if (zout + len > zout_end) if (!expand(len)) return 0; + p = (uint8 *) (zout - dist); + while (len--) + *zout++ = *p++; + } + } +} + +static int compute_huffman_codes(void) +{ + static uint8 length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static zhuffman z_codelength; // static just to save stack space + uint8 lencodes[286+32+137];//padding for maximum single op + uint8 codelength_sizes[19]; + int i,n; + + int hlit = zreceive(5) + 257; + int hdist = zreceive(5) + 1; + int hclen = zreceive(4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = zreceive(3); + codelength_sizes[length_dezigzag[i]] = (uint8) s; + } + if (!zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = zhuffman_decode(&z_codelength); + assert(c >= 0 && c < 19); + if (c < 16) + lencodes[n++] = (uint8) c; + else if (c == 16) { + c = zreceive(2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = zreceive(3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + assert(c == 18); + c = zreceive(7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return e("bad codelengths","Corrupt PNG"); + if (!zbuild_huffman(&z_length, lencodes, hlit)) return 0; + if (!zbuild_huffman(&z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int parse_uncompressed_block(void) +{ + uint8 header[4]; + int len,nlen,k; + if (num_bits & 7) + zreceive(num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (num_bits > 0) { + header[k++] = (uint8) (code_buffer & 255); // wtf this warns? + code_buffer >>= 8; + num_bits -= 8; + } + assert(num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = (uint8) zget8(); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return e("zlib corrupt","Corrupt PNG"); + if (zbuffer + len > zbuffer_end) return e("read past buffer","Corrupt PNG"); + if (zout + len > zout_end) + if (!expand(len)) return 0; + memcpy(zout, zbuffer, len); + zbuffer += len; + zout += len; + return 1; +} + +static int parse_zlib_header(void) +{ + int cmf = zget8(); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = zget8(); + if ((cmf*256+flg) % 31 != 0) return e("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return e("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return e("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static uint8 default_length[288], default_distance[32]; +static void init_defaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) default_length[i] = 8; + for ( ; i <= 255; ++i) default_length[i] = 9; + for ( ; i <= 279; ++i) default_length[i] = 7; + for ( ; i <= 287; ++i) default_length[i] = 8; + + for (i=0; i <= 31; ++i) default_distance[i] = 5; +} + +static int parse_zlib(int parse_header) +{ + int final, type; + if (parse_header) + if (!parse_zlib_header()) return 0; + num_bits = 0; + code_buffer = 0; + do { + final = zreceive(1); + type = zreceive(2); + if (type == 0) { + if (!parse_uncompressed_block()) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!default_length[0]) init_defaults(); + if (!zbuild_huffman(&z_length , default_length , 288)) return 0; + if (!zbuild_huffman(&z_distance, default_distance, 32)) return 0; + } else { + if (!compute_huffman_codes()) return 0; + } + if (!parse_huffman_block()) return 0; + } + } while (!final); + return 1; +} + +static int do_zlib(char *obuf, int olen, int exp, int parse_header) +{ + zout_start = obuf; + zout = obuf; + zout_end = obuf + olen; + z_expandable = exp; + + return parse_zlib(parse_header); +} + +char *stbi_zlib_decode_malloc_guesssize(int initial_size, int *outlen) +{ + char *p = (char *) malloc(initial_size); + if (p == NULL) return NULL; + if (do_zlib(p, initial_size, 1, 1)) { + *outlen = (int) (zout - zout_start); + return zout_start; + } else { + free(zout_start); + return NULL; + } +} + +char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + zbuffer = (uint8 *) buffer; + zbuffer_end = (uint8 *) buffer+len; + return stbi_zlib_decode_malloc_guesssize(16384, outlen); +} + +int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + zbuffer = (uint8 *) ibuffer; + zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(obuffer, olen, 0, 1)) + return (int) (zout - zout_start); + else + return -1; +} + +char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + char *p = (char *) malloc(16384); + if (p == NULL) return NULL; + zbuffer = (uint8 *) buffer; + zbuffer_end = (uint8 *) buffer+len; + if (do_zlib(p, 16384, 1, 0)) { + *outlen = (int) (zout - zout_start); + return zout_start; + } else { + free(zout_start); + return NULL; + } +} + +int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + zbuffer = (uint8 *) ibuffer; + zbuffer_end = (uint8 *) ibuffer + ilen; + if (do_zlib(obuffer, olen, 0, 0)) + return (int) (zout - zout_start); + else + return -1; +} + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + + +typedef struct +{ + uint32 length; + uint32 type; +} chunk; + +#define PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static chunk get_chunk_header(void) +{ + chunk c; + c.length = get32(); + c.type = get32(); + return c; +} + +static int check_png_header(void) +{ + static uint8 png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (get8() != png_sig[i]) return e("bad png sig","Not a PNG"); + return 1; +} + +static uint8 *idata, *expanded, *out; + +enum { + F_none=0, F_sub=1, F_up=2, F_avg=3, F_paeth=4, + F_avg_first, F_paeth_first, +}; + +static uint8 first_row_filter[5] = +{ + F_none, F_sub, F_none, F_avg_first, F_paeth_first +}; + +static int paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +// create the png data from post-deflated data +static int create_png_image(uint8 *raw, uint32 raw_len, int out_n) +{ + uint32 i,j,stride = img_x*out_n; + int k; + assert(out_n == img_n || out_n == img_n+1); + out = (uint8 *) malloc(img_x * img_y * out_n); + if (!out) return e("outofmem", "Out of memory"); + if (raw_len != (img_n * img_x + 1) * img_y) return e("not enough pixels","Corrupt PNG"); + for (j=0; j < img_y; ++j) { + uint8 *cur = out + stride*j; + uint8 *prior = cur - stride; + int filter = *raw++; + if (filter > 4) return e("invalid filter","Corrupt PNG"); + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + // handle first pixel explicitly + for (k=0; k < img_n; ++k) { + switch(filter) { + case F_none : cur[k] = raw[k]; break; + case F_sub : cur[k] = raw[k]; break; + case F_up : cur[k] = raw[k] + prior[k]; break; + case F_avg : cur[k] = raw[k] + (prior[k]>>1); break; + case F_paeth : cur[k] = (uint8) (raw[k] + paeth(0,prior[k],0)); break; + case F_avg_first : cur[k] = raw[k]; break; + case F_paeth_first: cur[k] = raw[k]; break; + } + } + if (img_n != out_n) cur[img_n] = 255; + raw += img_n; + cur += out_n; + prior += out_n; + // this is a little gross, so that we don't switch per-pixel or per-component + if (img_n == out_n) { + #define CASE(f) \ + case f: \ + for (i=1; i < img_x; ++i, raw+=img_n,cur+=img_n,prior+=img_n) \ + for (k=0; k < img_n; ++k) + switch(filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-img_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-img_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],prior[k],prior[k-img_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-img_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-img_n],0,0)); break; + } + #undef CASE + } else { + assert(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=1; i < img_x; ++i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \ + for (k=0; k < img_n; ++k) + switch(filter) { + CASE(F_none) cur[k] = raw[k]; break; + CASE(F_sub) cur[k] = raw[k] + cur[k-out_n]; break; + CASE(F_up) cur[k] = raw[k] + prior[k]; break; + CASE(F_avg) cur[k] = raw[k] + ((prior[k] + cur[k-out_n])>>1); break; + CASE(F_paeth) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],prior[k],prior[k-out_n])); break; + CASE(F_avg_first) cur[k] = raw[k] + (cur[k-out_n] >> 1); break; + CASE(F_paeth_first) cur[k] = (uint8) (raw[k] + paeth(cur[k-out_n],0,0)); break; + } + #undef CASE + } + } + return 1; +} + +static int compute_transparency(uint8 tc[3], int out_n) +{ + uint32 i, pixel_count = img_x * img_y; + uint8 *p = out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + assert(out_n == 2 || out_n == 4); + + p = out; + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int expand_palette(uint8 *palette, int len, int pal_img_n) +{ + uint32 i, pixel_count = img_x * img_y; + uint8 *p, *temp_out, *orig = out; + + p = (uint8 *) malloc(pixel_count * pal_img_n); + if (p == NULL) return e("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + free(out); + out = temp_out; + return 1; +} + +static int parse_png_file(int scan, int req_comp) +{ + uint8 palette[1024], pal_img_n=0; + uint8 has_trans=0, tc[3]; + uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k; + + if (!check_png_header()) return 0; + + if (scan == SCAN_type) return 1; + + for(;;first=0) { + chunk c = get_chunk_header(); + if (first && c.type != PNG_TYPE('I','H','D','R')) + return e("first not IHDR","Corrupt PNG"); + switch (c.type) { + case PNG_TYPE('I','H','D','R'): { + int depth,color,interlace,comp,filter; + if (!first) return e("multiple IHDR","Corrupt PNG"); + if (c.length != 13) return e("bad IHDR len","Corrupt PNG"); + img_x = get32(); if (img_x > (1 << 24)) return e("too large","Very large image (corrupt?)"); + img_y = get32(); if (img_y > (1 << 24)) return e("too large","Very large image (corrupt?)"); + depth = get8(); if (depth != 8) return e("8bit only","PNG not supported: 8-bit only"); + color = get8(); if (color > 6) return e("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG"); + comp = get8(); if (comp) return e("bad comp method","Corrupt PNG"); + filter= get8(); if (filter) return e("bad filter method","Corrupt PNG"); + interlace = get8(); if (interlace) return e("interlaced","PNG not supported: interlaced mode"); + if (!img_x || !img_y) return e("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / img_x / img_n < img_y) return e("too large", "Image too large to decode"); + if (scan == SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + img_n = 1; + if ((1 << 30) / img_x / 4 < img_y) return e("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case PNG_TYPE('P','L','T','E'): { + if (c.length > 256*3) return e("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return e("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = get8u(); + palette[i*4+1] = get8u(); + palette[i*4+2] = get8u(); + palette[i*4+3] = 255; + } + break; + } + + case PNG_TYPE('t','R','N','S'): { + if (idata) return e("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == SCAN_header) { img_n = 4; return 1; } + if (pal_len == 0) return e("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return e("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = get8u(); + } else { + if (!(img_n & 1)) return e("tRNS with alpha","Corrupt PNG"); + if (c.length != (uint32) img_n*2) return e("bad tRNS len","Corrupt PNG"); + has_trans = 1; + for (k=0; k < img_n; ++k) + tc[k] = (uint8) get16(); // non 8-bit images will be larger + } + break; + } + + case PNG_TYPE('I','D','A','T'): { + if (pal_img_n && !pal_len) return e("no PLTE","Corrupt PNG"); + if (scan == SCAN_header) { img_n = pal_img_n; return 1; } + if (ioff + c.length > idata_limit) { + uint8 *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + p = (uint8 *) realloc(idata, idata_limit); if (p == NULL) return e("outofmem", "Out of memory"); + idata = p; + } + #ifndef STBI_NO_STDIO + if (img_file) + { + if (fread(idata+ioff,1,c.length,img_file) != c.length) return e("outofdata","Corrupt PNG"); + } + else + #endif + { + memcpy(idata+ioff, img_buffer, c.length); + img_buffer += c.length; + } + ioff += c.length; + break; + } + + case PNG_TYPE('I','E','N','D'): { + uint32 raw_len; + if (scan != SCAN_load) return 1; + if (idata == NULL) return e("no IDAT","Corrupt PNG"); + expanded = (uint8 *) stbi_zlib_decode_malloc((char *) idata, ioff, (int *) &raw_len); + if (expanded == NULL) return 0; // zlib should set error + free(idata); idata = NULL; + if ((req_comp == img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + img_out_n = img_n+1; + else + img_out_n = img_n; + if (!create_png_image(expanded, raw_len, img_out_n)) return 0; + if (has_trans) + if (!compute_transparency(tc, img_out_n)) return 0; + if (pal_img_n) { + // pal_img_n == 3 or 4 + img_n = pal_img_n; // record the actual colors we had + img_out_n = pal_img_n; + if (req_comp >= 3) img_out_n = req_comp; + if (!expand_palette(palette, pal_len, img_out_n)) + return 0; + } + free(expanded); expanded = NULL; + return 1; + } + + default: + // if critical, fail + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + static char invalid_chunk[] = "XXXX chunk not known"; + invalid_chunk[0] = (uint8) (c.type >> 24); + invalid_chunk[1] = (uint8) (c.type >> 16); + invalid_chunk[2] = (uint8) (c.type >> 8); + invalid_chunk[3] = (uint8) (c.type >> 0); + #endif + return e(invalid_chunk, "PNG not supported: unknown chunk type"); + } + skip(c.length); + break; + } + // end of chunk, read and skip CRC + get8(); get8(); get8(); get8(); + } +} + +static unsigned char *do_png(int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return epuc("bad req_comp", "Internal error"); + if (parse_png_file(SCAN_load, req_comp)) { + result = out; + out = NULL; + if (req_comp && req_comp != img_out_n) { + result = convert_format(result, img_out_n, req_comp); + if (result == NULL) return result; + } + *x = img_x; + *y = img_y; + if (n) *n = img_n; + } + free(out); out = NULL; + free(expanded); expanded = NULL; + free(idata); idata = NULL; + + return result; +} + +#ifndef STBI_NO_STDIO +unsigned char *stbi_png_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return do_png(x,y,comp,req_comp); +} + +unsigned char *stbi_png_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_png_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return data; +} +#endif + +unsigned char *stbi_png_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer,len); + return do_png(x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +int stbi_png_test_file(FILE *f) +{ + int n,r; + n = ftell(f); + start_file(f); + r = parse_png_file(SCAN_type,STBI_default); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_png_test_memory(stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return parse_png_file(SCAN_type,STBI_default); +} + +// TODO: load header from png +#ifndef STBI_NO_STDIO +extern int stbi_png_info (char const *filename, int *x, int *y, int *comp); +extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp); +#endif +extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp); + +// Microsoft/Windows BMP image + +static int bmp_test(void) +{ + int sz; + if (get8() != 'B') return 0; + if (get8() != 'M') return 0; + get32le(); // discard filesize + get16le(); // discard reserved + get16le(); // discard reserved + get32le(); // discard data offset + sz = get32le(); + if (sz == 12 || sz == 40 || sz == 56 || sz == 108) return 1; + return 0; +} + +#ifndef STBI_NO_STDIO +int stbi_bmp_test_file (FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = bmp_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_bmp_test_memory (stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return bmp_test(); +} + +// returns 0..31 for the highest set bit +static int high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +static stbi_uc *bmp_load(int *x, int *y, int *comp, int req_comp) +{ + unsigned int mr=0,mg=0,mb=0,ma=0; + stbi_uc pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + if (get8() != 'B' || get8() != 'M') return epuc("not BMP", "Corrupt BMP"); + get32le(); // discard filesize + get16le(); // discard reserved + get16le(); // discard reserved + offset = get32le(); + hsz = get32le(); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108) return epuc("unknown BMP", "BMP type not supported: unknown"); + failure_reason = "bad BMP"; + if (hsz == 12) { + img_x = get16le(); + img_y = get16le(); + } else { + img_x = get32le(); + img_y = get32le(); + } + if (get16le() != 1) return 0; + bpp = get16le(); + if (bpp == 1) return epuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) img_y) > 0; + img_y = abs((int) img_y); + if (hsz == 12) { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } else { + compress = get32le(); + if (compress == 1 || compress == 2) return epuc("BMP RLE", "BMP type not supported: RLE"); + get32le(); // discard sizeof + get32le(); // discard hres + get32le(); // discard vres + get32le(); // discard colorsused + get32le(); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + get32le(); + get32le(); + get32le(); + get32le(); + } + if (bpp == 16 || bpp == 32) { + mr = mg = mb = 0; + if (compress == 0) { + if (bpp == 32) { + mr = 0xff << 16; + mg = 0xff << 8; + mb = 0xff << 0; + } else { + mr = 31 << 10; + mg = 31 << 5; + mb = 31 << 0; + } + } else if (compress == 3) { + mr = get32le(); + mg = get32le(); + mb = get32le(); + // not documented, but generated by photoshop and handled by mspaint + if (mr == mg && mg == mb) { + // ?!?!? + return NULL; + } + } else + return NULL; + } + } else { + assert(hsz == 108); + mr = get32le(); + mg = get32le(); + mb = get32le(); + ma = get32le(); + get32le(); // discard color space + for (i=0; i < 12; ++i) + get32le(); // discard color space parameters + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = img_n; // if they want monochrome, we'll post-convert + out = (stbi_uc *) malloc(target * img_x * img_y); + if (!out) return epuc("outofmem", "Out of memory"); + if (bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) return epuc("invalid", "Corrupt BMP"); + for (i=0; i < psize; ++i) { + pal[i][2] = get8(); + pal[i][1] = get8(); + pal[i][0] = get8(); + if (hsz != 12) get8(); + pal[i][3] = 255; + } + skip(offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) width = (img_x + 1) >> 1; + else if (bpp == 8) width = img_x; + else return epuc("bad bpp", "Corrupt BMP"); + pad = (-width)&3; + for (j=0; j < (int) img_y; ++j) { + for (i=0; i < (int) img_x; i += 2) { + int v=get8(),v2=0; + if (bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) img_x) break; + v = (bpp == 8) ? get8() : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + skip(pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + skip(offset - 14 - hsz); + if (bpp == 24) width = 3 * img_x; + else if (bpp == 16) width = 2*img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (bpp == 24) { + easy = 1; + } else if (bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0xff000000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) return epuc("bad masks", "Corrupt BMP"); + // right shift amt to put high bit in position #7 + rshift = high_bit(mr)-7; rcount = bitcount(mr); + gshift = high_bit(mg)-7; gcount = bitcount(mr); + bshift = high_bit(mb)-7; bcount = bitcount(mr); + ashift = high_bit(ma)-7; acount = bitcount(mr); + } + for (j=0; j < (int) img_y; ++j) { + if (easy) { + for (i=0; i < (int) img_x; ++i) { + int a; + out[z+2] = get8(); + out[z+1] = get8(); + out[z+0] = get8(); + z += 3; + a = (easy == 2 ? get8() : 255); + if (target == 4) out[z++] = a; + } + } else { + for (i=0; i < (int) img_x; ++i) { + uint32 v = (bpp == 16 ? get16le() : get32le()); + int a; + out[z++] = shiftsigned(v & mr, rshift, rcount); + out[z++] = shiftsigned(v & mg, gshift, gcount); + out[z++] = shiftsigned(v & mb, bshift, bcount); + a = (ma ? shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = a; + } + } + skip(pad); + } + } + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) img_y>>1; ++j) { + stbi_uc *p1 = out + j *img_x*target; + stbi_uc *p2 = out + (img_y-1-j)*img_x*target; + for (i=0; i < (int) img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = convert_format(out, target, req_comp); + if (out == NULL) return out; // convert_format frees input on failure + } + + *x = img_x; + *y = img_y; + if (comp) *comp = target; + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_bmp_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_bmp_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_bmp_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return bmp_load(x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_bmp_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return bmp_load(x,y,comp,req_comp); +} + +// Targa Truevision - TGA +// by Jonathan Dummer + +static int tga_test(void) +{ + int sz; + get8u(); // discard Offset + sz = get8u(); // color type + if( sz > 1 ) return 0; // only RGB or indexed allowed + sz = get8u(); // image type + if( (sz != 1) && (sz != 2) && (sz != 3) && (sz != 9) && (sz != 10) && (sz != 11) ) return 0; // only RGB or grey allowed, +/- RLE + get16(); // discard palette start + get16(); // discard palette length + get8(); // discard bits per palette color entry + get16(); // discard x origin + get16(); // discard y origin + if( get16() < 1 ) return 0; // test width + if( get16() < 1 ) return 0; // test height + sz = get8(); // bits per pixel + if( (sz != 8) && (sz != 16) && (sz != 24) && (sz != 32) ) return 0; // only RGB or RGBA or grey allowed + return 1; // seems to have passed everything +} + +#ifndef STBI_NO_STDIO +int stbi_tga_test_file (FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = tga_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_tga_test_memory (stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return tga_test(); +} + +static stbi_uc *tga_load(int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = get8u(); + int tga_indexed = get8u(); + int tga_image_type = get8u(); + int tga_is_RLE = 0; + int tga_palette_start = get16le(); + int tga_palette_len = get16le(); + int tga_palette_bits = get8u(); + int tga_x_origin = get16le(); + int tga_y_origin = get16le(); + int tga_width = get16le(); + int tga_height = get16le(); + int tga_bits_per_pixel = get8u(); + int tga_inverted = get8u(); + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + unsigned char trans_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + // do a tiny bit of precessing + if( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + /* int tga_alpha_bits = tga_inverted & 15; */ + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // error check + if( //(tga_indexed) || + (tga_width < 1) || (tga_height < 1) || + (tga_image_type < 1) || (tga_image_type > 3) || + ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16) && + (tga_bits_per_pixel != 24) && (tga_bits_per_pixel != 32)) + ) + { + return NULL; + } + + // If I'm paletted, then I'll use the number of bits from the palette + if( tga_indexed ) + { + tga_bits_per_pixel = tga_palette_bits; + } + + // tga info + *x = tga_width; + *y = tga_height; + if( (req_comp < 1) || (req_comp > 4) ) + { + // just use whatever the file was + req_comp = tga_bits_per_pixel / 8; + *comp = req_comp; + } else + { + // force a new number of components + *comp = tga_bits_per_pixel/8; + } + tga_data = (unsigned char*)malloc( tga_width * tga_height * req_comp ); + + // skip to the data's starting position (offset usually = 0) + skip( tga_offset ); + // do I need to load a palette? + if( tga_indexed ) + { + // any data to skip? (offset usually = 0) + skip( tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)malloc( tga_palette_len * tga_palette_bits / 8 ); + getn( tga_palette, tga_palette_len * tga_palette_bits / 8 ); + } + // load the data + for( i = 0; i < tga_width * tga_height; ++i ) + { + // if I'm in RLE mode, do I need to get a RLE chunk? + if( tga_is_RLE ) + { + if( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = get8u(); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if( read_next_pixel ) + { + // load however much data we did have + if( tga_indexed ) + { + // read in 1 byte, then perform the lookup + int pal_idx = get8u(); + if( pal_idx >= tga_palette_len ) + { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_bits_per_pixel / 8; + for( j = 0; j*8 < tga_bits_per_pixel; ++j ) + { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else + { + // read in the data raw + for( j = 0; j*8 < tga_bits_per_pixel; ++j ) + { + raw_data[j] = get8u(); + } + } + // convert raw to the intermediate format + switch( tga_bits_per_pixel ) + { + case 8: + // Luminous => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 16: + // Luminous,Alpha => RGBA + trans_data[0] = raw_data[0]; + trans_data[1] = raw_data[0]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[1]; + break; + case 24: + // BGR => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = 255; + break; + case 32: + // BGRA => RGBA + trans_data[0] = raw_data[2]; + trans_data[1] = raw_data[1]; + trans_data[2] = raw_data[0]; + trans_data[3] = raw_data[3]; + break; + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + // convert to final format + switch( req_comp ) + { + case 1: + // RGBA => Luminance + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + break; + case 2: + // RGBA => Luminance,Alpha + tga_data[i*req_comp+0] = compute_y(trans_data[0],trans_data[1],trans_data[2]); + tga_data[i*req_comp+1] = trans_data[3]; + break; + case 3: + // RGBA => RGB + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + break; + case 4: + // RGBA => RGBA + tga_data[i*req_comp+0] = trans_data[0]; + tga_data[i*req_comp+1] = trans_data[1]; + tga_data[i*req_comp+2] = trans_data[2]; + tga_data[i*req_comp+3] = trans_data[3]; + break; + } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if( tga_inverted ) + { + for( j = 0; j*2 < tga_height; ++j ) + { + int index1 = j * tga_width * req_comp; + int index2 = (tga_height - 1 - j) * tga_width * req_comp; + for( i = tga_width * req_comp; i > 0; --i ) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if( tga_palette != NULL ) + { + free( tga_palette ); + } + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_tga_load (char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_tga_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_tga_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return tga_load(x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_tga_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return tga_load(x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicholas Schulz, tweaked by STB + +static int psd_test(void) +{ + if (get32() != 0x38425053) return 0; // "8BPS" + else return 1; +} + +#ifndef STBI_NO_STDIO +int stbi_psd_test_file(FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = psd_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +int stbi_psd_test_memory(stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return psd_test(); +} + +static stbi_uc *psd_load(int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int w,h; + + // Check identifier + if (get32() != 0x38425053) // "8BPS" + return epuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (get16() != 1) + return epuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + skip( 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = get16(); + if (channelCount < 0 || channelCount > 16) + return epuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = get32(); + w = get32(); + + // Make sure the depth is 8 bits. + if (get16() != 8) + return epuc("unsupported bit depth", "PSD bit depth is not 8 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (get16() != 3) + return epuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + skip(get32() ); + + // Skip the image resources. (resolution, pen tool paths, etc) + skip( get32() ); + + // Skip the reserved data. + skip( get32() ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = get16(); + if (compression > 1) + return epuc("unknown compression type", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) malloc(4 * w*h); + if (!out) return epuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + skip( h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = (channel == 3 ? 255 : 0), p += 4; + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = get8(); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = get8(); + p += 4; + len--; + } + } else if (len > 128) { + uint32 val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = get8(); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + uint8 *p; + + p = out + channel; + if (channel > channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++) *p = channel == 3 ? 255 : 0, p += 4; + } else { + // Read the data. + count = 0; + for (i = 0; i < pixelCount; i++) + *p = get8(), p += 4; + } + } + } + + if (req_comp && req_comp != 4) { + img_x = w; + img_y = h; + out = convert_format(out, 4, req_comp); + if (out == NULL) return out; // convert_format frees input on failure + } + + if (comp) *comp = channelCount; + *y = h; + *x = w; + + return out; +} + +#ifndef STBI_NO_STDIO +stbi_uc *stbi_psd_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *data; + FILE *f = fopen(filename, "rb"); + if (!f) return NULL; + data = stbi_psd_load_from_file(f, x,y,comp,req_comp); + fclose(f); + return data; +} + +stbi_uc *stbi_psd_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return psd_load(x,y,comp,req_comp); +} +#endif + +stbi_uc *stbi_psd_load_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return psd_load(x,y,comp,req_comp); +} + + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int hdr_test(void) +{ + char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (get8() != signature[i]) + return 0; + return 1; +} + +int stbi_hdr_test_memory(stbi_uc const *buffer, int len) +{ + start_mem(buffer, len); + return hdr_test(); +} + +#ifndef STBI_NO_STDIO +int stbi_hdr_test_file(FILE *f) +{ + int r,n = ftell(f); + start_file(f); + r = hdr_test(); + fseek(f,n,SEEK_SET); + return r; +} +#endif + +#define HDR_BUFLEN 1024 +static char *hdr_gettoken(char *buffer) +{ + int len=0; + char *s = buffer, c = '\0'; + + c = get8(); + + while (!at_eof() && c != '\n') { + buffer[len++] = c; + if (len == HDR_BUFLEN-1) { + // flush to end of line + while (!at_eof() && get8() != '\n') + ; + break; + } + c = get8(); + } + + buffer[len] = 0; + return buffer; +} + +static void hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + + +static float *hdr_load(int *x, int *y, int *comp, int req_comp) +{ + char buffer[HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(hdr_gettoken(buffer), "#?RADIANCE") != 0) + return epf("not HDR", "Corrupt HDR image"); + + // Parse header + while(1) { + token = hdr_gettoken(buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return epf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = hdr_gettoken(buffer); + if (strncmp(token, "-Y ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return epf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = strtol(token, NULL, 10); + + *x = width; + *y = height; + + *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + getn(rgbe, 4); + hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = get8(); + c2 = get8(); + len = get8(); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4] = { c1,c2,len, get8() }; + hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + free(scanline); + goto main_decode_loop; // yes, this is fucking insane; blame the fucking insane format + } + len <<= 8; + len |= get8(); + if (len != width) { free(hdr_data); free(scanline); return epf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = get8(); + if (count > 128) { + // Run + value = get8(); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = get8(); + } + } + } + for (i=0; i < width; ++i) + hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + free(scanline); + } + + return hdr_data; +} + +#ifndef STBI_NO_STDIO +float *stbi_hdr_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + start_file(f); + return hdr_load(x,y,comp,req_comp); +} +#endif + +float *stbi_hdr_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + start_mem(buffer, len); + return hdr_load(x,y,comp,req_comp); +} + +#endif // STBI_NO_HDR + +/////////////////////// write image /////////////////////// + +#ifndef STBI_NO_WRITE + +static void write8(FILE *f, int x) { uint8 z = (uint8) x; fwrite(&z,1,1,f); } + +static void writefv(FILE *f, char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { uint8 x = va_arg(v, int); write8(f,x); break; } + case '2': { int16 x = va_arg(v, int); write8(f,x); write8(f,x>>8); break; } + case '4': { int32 x = va_arg(v, int); write8(f,x); write8(f,x>>8); write8(f,x>>16); write8(f,x>>24); break; } + default: + assert(0); + va_end(v); + return; + } + } +} + +static void writef(FILE *f, char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + writefv(f,fmt,v); + va_end(v); +} + +static void write_pixels(FILE *f, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad) +{ + uint8 bg[3] = { 255, 0, 255}, px[3]; + uint32 zero = 0; + int i,j,k, j_end; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + uint8 *d = (uint8 *) data + (j*x+i)*comp; + if (write_alpha < 0) + fwrite(&d[comp-1], 1, 1, f); + switch (comp) { + case 1: + case 2: writef(f, "111", d[0],d[0],d[0]); + break; + case 4: + if (!write_alpha) { + for (k=0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3])/255; + writef(f, "111", px[1-rgb_dir],px[1],px[1+rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + writef(f, "111", d[1-rgb_dir],d[1],d[1+rgb_dir]); + break; + } + if (write_alpha > 0) + fwrite(&d[comp-1], 1, 1, f); + } + fwrite(&zero,scanline_pad,1,f); + } +} + +static int outfile(char const *filename, int rgb_dir, int vdir, int x, int y, int comp, void *data, int alpha, int pad, char *fmt, ...) +{ + FILE *f = fopen(filename, "wb"); + if (f) { + va_list v; + va_start(v, fmt); + writefv(f, fmt, v); + va_end(v); + write_pixels(f,rgb_dir,vdir,x,y,comp,data,alpha,pad); + fclose(f); + } + return f != NULL; +} + +int stbi_write_bmp(char const *filename, int x, int y, int comp, void *data) +{ + int pad = (-x*3) & 3; + return outfile(filename,-1,-1,x,y,comp,data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +int stbi_write_tga(char const *filename, int x, int y, int comp, void *data) +{ + int has_alpha = !(comp & 1); + return outfile(filename, -1,-1, x, y, comp, data, has_alpha, 0, + "111 221 2222 11", 0,0,2, 0,0,0, 0,0,x,y, 24+8*has_alpha, 8*has_alpha); +} + +// any other image formats that do interleaved rgb data? +// PNG: requires adler32,crc32 -- significant amount of code +// PSD: no, channels output separately +// TIFF: no, stripwise-interleaved... i think + +#endif // STBI_NO_WRITE diff --git a/clutter/cogl/gl/Makefile.am b/clutter/cogl/gl/Makefile.am index 98949b261..17e5ed465 100644 --- a/clutter/cogl/gl/Makefile.am +++ b/clutter/cogl/gl/Makefile.am @@ -1,19 +1,36 @@ -libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/clutter -libclutterinclude_HEADERS = $(top_srcdir)/clutter/cogl/cogl.h \ - $(top_srcdir)/clutter/cogl/gl/cogl-defines.h +libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/cogl +libclutterinclude_HEADERS = $(top_builddir)/clutter/cogl/cogl.h \ + $(top_builddir)/clutter/cogl/cogl-defines-gl.h INCLUDES = \ - -I$(top_srcdir) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/clutter \ -I$(top_srcdir)/clutter/cogl \ - $(CLUTTER_CFLAGS) \ - $(CLUTTER_DEBUG_CFLAGS) \ + -I$(top_srcdir)/clutter/cogl/common \ + -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ + -I$(top_builddir)/clutter \ + -I$(top_builddir)/clutter/cogl \ + $(CLUTTER_CFLAGS) \ + $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) LDADD = $(CLUTTER_LIBS) noinst_LTLIBRARIES = libclutter-cogl.la -libclutter_cogl_la_SOURCES = \ - $(top_srcdir)/clutter/cogl/cogl.h \ - $(top_srcdir)/clutter/cogl/gl/cogl-defines.h \ - cogl.c +libclutter_cogl_la_SOURCES = \ + $(top_builddir)/clutter/cogl/cogl.h \ + $(top_builddir)/clutter/cogl/gl/cogl-defines.h \ + cogl-defines.h \ + cogl-internal.h \ + cogl-primitives.h \ + cogl-texture.h \ + cogl-fbo.h \ + cogl-context.h \ + cogl.c \ + cogl-primitives.c \ + cogl-texture.c \ + cogl-fbo.c \ + cogl-context.c + +libclutter_cogl_la_LIBADD = $(top_builddir)/clutter/cogl/common/libclutter-cogl-common.la diff --git a/clutter/cogl/gl/cogl-context.c b/clutter/cogl/gl/cogl-context.c new file mode 100644 index 000000000..936d7e9bf --- /dev/null +++ b/clutter/cogl/gl/cogl-context.c @@ -0,0 +1,114 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" + +#include + +static CoglContext *_context = NULL; + +gboolean +cogl_create_context () +{ + if (_context != NULL) + return FALSE; + + /* Allocate context memory */ + _context = (CoglContext*) g_malloc (sizeof (CoglContext)); + + /* Init default values */ + _context->feature_flags = 0; + _context->features_cached = FALSE; + + _context->enable_flags = 0; + _context->color_alpha = 255; + + _context->path_nodes = NULL; + _context->path_nodes_cap = 0; + _context->path_nodes_size = 0; + + _context->texture_handles = NULL; + + _context->fbo_handles = NULL; + _context->draw_buffer = COGL_WINDOW_BUFFER; + + _context->pf_glGenRenderbuffersEXT = NULL; + _context->pf_glBindRenderbufferEXT = NULL; + _context->pf_glRenderbufferStorageEXT = NULL; + _context->pf_glGenFramebuffersEXT = NULL; + _context->pf_glBindFramebufferEXT = NULL; + _context->pf_glFramebufferTexture2DEXT = NULL; + _context->pf_glFramebufferRenderbufferEXT = NULL; + _context->pf_glCheckFramebufferStatusEXT = NULL; + _context->pf_glDeleteFramebuffersEXT = NULL; + _context->pf_glBlitFramebufferEXT = NULL; + _context->pf_glRenderbufferStorageMultisampleEXT = NULL; + + _context->pf_glCreateProgramObjectARB = NULL; + _context->pf_glCreateShaderObjectARB = NULL; + _context->pf_glShaderSourceARB = NULL; + _context->pf_glCompileShaderARB = NULL; + _context->pf_glAttachObjectARB = NULL; + _context->pf_glLinkProgramARB = NULL; + _context->pf_glUseProgramObjectARB = NULL; + _context->pf_glGetUniformLocationARB = NULL; + _context->pf_glDeleteObjectARB = NULL; + _context->pf_glGetInfoLogARB = NULL; + _context->pf_glGetObjectParameterivARB = NULL; + _context->pf_glUniform1fARB = NULL; + + /* Init OpenGL state */ + glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColorMask (TRUE, TRUE, TRUE, FALSE); + cogl_enable (0); + + return TRUE; +} + +void +cogl_destroy_context () +{ + if (_context == NULL) + return; + + g_free (_context); +} + +CoglContext * +_cogl_context_get_default () +{ + /* Create if doesn't exist yet */ + if (_context == NULL) + cogl_create_context (); + + return _context; +} diff --git a/clutter/cogl/gl/cogl-context.h b/clutter/cogl/gl/cogl-context.h new file mode 100644 index 000000000..4e952cc5f --- /dev/null +++ b/clutter/cogl/gl/cogl-context.h @@ -0,0 +1,95 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_CONTEXT_H +#define __COGL_CONTEXT_H + +#include "cogl-primitives.h" + +typedef struct +{ + /* Features cache */ + CoglFeatureFlags feature_flags; + gboolean features_cached; + + /* Enable cache */ + gulong enable_flags; + guint8 color_alpha; + + /* Primitives */ + CoglFixedVec2 path_start; + CoglFixedVec2 path_pen; + CoglFloatVec2 *path_nodes; + guint path_nodes_cap; + guint path_nodes_size; + CoglFixedVec2 path_nodes_min; + CoglFixedVec2 path_nodes_max; + + /* Textures */ + GArray *texture_handles; + + /* Framebuffer objects */ + GArray *fbo_handles; + CoglBufferTarget draw_buffer; + + /* Relying on glext.h to define these */ + PFNGLGENRENDERBUFFERSEXTPROC pf_glGenRenderbuffersEXT; + PFNGLBINDRENDERBUFFEREXTPROC pf_glBindRenderbufferEXT; + PFNGLRENDERBUFFERSTORAGEEXTPROC pf_glRenderbufferStorageEXT; + PFNGLGENFRAMEBUFFERSEXTPROC pf_glGenFramebuffersEXT; + PFNGLBINDFRAMEBUFFEREXTPROC pf_glBindFramebufferEXT; + PFNGLFRAMEBUFFERTEXTURE2DEXTPROC pf_glFramebufferTexture2DEXT; + PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC pf_glFramebufferRenderbufferEXT; + PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC pf_glCheckFramebufferStatusEXT; + PFNGLDELETEFRAMEBUFFERSEXTPROC pf_glDeleteFramebuffersEXT; + PFNGLBLITFRAMEBUFFEREXTPROC pf_glBlitFramebufferEXT; + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC pf_glRenderbufferStorageMultisampleEXT; + + PFNGLCREATEPROGRAMOBJECTARBPROC pf_glCreateProgramObjectARB; + PFNGLCREATESHADEROBJECTARBPROC pf_glCreateShaderObjectARB; + PFNGLSHADERSOURCEARBPROC pf_glShaderSourceARB; + PFNGLCOMPILESHADERARBPROC pf_glCompileShaderARB; + PFNGLATTACHOBJECTARBPROC pf_glAttachObjectARB; + PFNGLLINKPROGRAMARBPROC pf_glLinkProgramARB; + PFNGLUSEPROGRAMOBJECTARBPROC pf_glUseProgramObjectARB; + PFNGLGETUNIFORMLOCATIONARBPROC pf_glGetUniformLocationARB; + PFNGLDELETEOBJECTARBPROC pf_glDeleteObjectARB; + PFNGLGETINFOLOGARBPROC pf_glGetInfoLogARB; + PFNGLGETOBJECTPARAMETERIVARBPROC pf_glGetObjectParameterivARB; + PFNGLUNIFORM1FARBPROC pf_glUniform1fARB; + +} CoglContext; + +CoglContext * +_cogl_context_get_default (); + +/* Obtains the context and returns retval if NULL */ +#define _COGL_GET_CONTEXT(ctxvar, retval) \ +CoglContext *ctxvar = _cogl_context_get_default (); \ +if (ctxvar == NULL) return retval; + +#define NO_RETVAL + +#endif /* __COGL_CONTEXT_H */ diff --git a/clutter/cogl/gl/cogl-fbo.c b/clutter/cogl/gl/cogl-fbo.c new file mode 100644 index 000000000..f4fb968d5 --- /dev/null +++ b/clutter/cogl/gl/cogl-fbo.c @@ -0,0 +1,390 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture.h" +#include "cogl-fbo.h" +#include "cogl-context.h" + +/* Expecting EXT functions not to be defined - redirect to pointers in context */ +#define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT +#define glBindRenderbufferEXT ctx->pf_glBindRenderbufferEXT +#define glRenderbufferStorageEXT ctx->pf_glRenderbufferStorageEXT +#define glGenFramebuffersEXT ctx->pf_glGenFramebuffersEXT +#define glBindFramebufferEXT ctx->pf_glBindFramebufferEXT +#define glFramebufferTexture2DEXT ctx->pf_glFramebufferTexture2DEXT +#define glFramebufferRenderbufferEXT ctx->pf_glFramebufferRenderbufferEXT +#define glCheckFramebufferStatusEXT ctx->pf_glCheckFramebufferStatusEXT +#define glDeleteFramebuffersEXT ctx->pf_glDeleteFramebuffersEXT +#define glBlitFramebufferEXT ctx->pf_glBlitFramebufferEXT +#define glRenderbufferStorageMultisampleEXT ctx->pf_glRenderbufferStorageMultisampleEXT + +static gint +_cogl_fbo_handle_find (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, -1); + + gint i; + + if (ctx->fbo_handles == NULL) + return -1; + + for (i=0; i < ctx->fbo_handles->len; ++i) + if (g_array_index (ctx->fbo_handles, CoglHandle, i) == handle) + return i; + + return -1; +} + +static CoglHandle +_cogl_fbo_handle_new (CoglFbo *fbo) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + CoglHandle handle = (CoglHandle)fbo; + + if (ctx->fbo_handles == NULL) + ctx->fbo_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); + + g_array_append_val (ctx->fbo_handles, handle); + + return handle; +} + +static void +_cogl_fbo_handle_release (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + gint i; + + if ( (i = _cogl_fbo_handle_find (handle)) == -1) + return; + + g_array_remove_index_fast (ctx->fbo_handles, i); +} + +static CoglFbo* +_cogl_fbo_pointer_from_handle (CoglHandle handle) +{ + return (CoglFbo*) handle; +} + +gboolean +cogl_is_offscreen_buffer (CoglHandle handle) +{ + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return _cogl_fbo_handle_find (handle) >= 0; +} + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN)) + return COGL_INVALID_HANDLE; + + CoglTexture *tex; + CoglFbo *fbo; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint tex_gl_handle; + GLuint fbo_gl_handle; + GLenum status; + + /* Make texhandle is a valid texture object */ + if (!cogl_is_texture (texhandle)) + return COGL_INVALID_HANDLE; + + tex = _cogl_texture_pointer_from_handle (texhandle); + + /* The texture must not be sliced */ + if (tex->slice_gl_handles == NULL) + return COGL_INVALID_HANDLE; + + if (tex->slice_gl_handles->len != 1) + return COGL_INVALID_HANDLE; + + /* Pick the single texture slice width, height and GL id */ + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + tex_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + + /* Generate framebuffer */ + glGenFramebuffersEXT (1, &fbo_gl_handle); + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo_gl_handle) ); + GE( glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + tex->gl_target, tex_gl_handle, 0) ); + + /* Make sure it's complete */ + status = glCheckFramebufferStatusEXT (GL_FRAMEBUFFER_EXT); + + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) + { + GE( glDeleteFramebuffersEXT (1, &fbo_gl_handle) ); + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); + return COGL_INVALID_HANDLE; + } + + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); + + /* Allocate and init a CoglFbo object (store non-wasted size + for subsequent blits and viewport setup) */ + fbo = (CoglFbo*) g_malloc (sizeof (CoglFbo)); + fbo->ref_count = 1; + fbo->width = x_span->size - x_span->waste; + fbo->height = y_span->size - y_span->waste; + fbo->gl_handle = fbo_gl_handle; + + return _cogl_fbo_handle_new (fbo); +} + +CoglHandle +cogl_offscreen_new_multisample () +{ + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_MULTISAMPLE)) + return COGL_INVALID_HANDLE; + + return COGL_INVALID_HANDLE; +} + +CoglHandle +cogl_offscreen_ref (CoglHandle handle) +{ + CoglFbo *fbo; + + if (!cogl_is_offscreen_buffer (handle)) + return COGL_INVALID_HANDLE; + + fbo = _cogl_fbo_pointer_from_handle (handle); + + fbo->ref_count++; + + return handle; +} + +void +cogl_offscreen_unref (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglFbo *fbo; + + /* Make sure this is a valid fbo handle */ + if (!cogl_is_offscreen_buffer (handle)) + return; + + fbo = _cogl_fbo_pointer_from_handle (handle); + + if (--fbo->ref_count < 1) + { + /* Release handle and destroy object */ + GE( glDeleteFramebuffersEXT (1, &fbo->gl_handle) ); + + _cogl_fbo_handle_release (handle); + + g_free (fbo); + } +} + +void +cogl_offscreen_blit_region (CoglHandle src_buffer, + CoglHandle dst_buffer, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT)) + return; + + CoglFbo *src_fbo; + CoglFbo *dst_fbo; + + /* Make sure these are valid fbo handles */ + if (!cogl_is_offscreen_buffer (src_buffer)) + return; + + if (!cogl_is_offscreen_buffer (dst_buffer)) + return; + + src_fbo = _cogl_fbo_pointer_from_handle (src_buffer); + dst_fbo = _cogl_fbo_pointer_from_handle (dst_buffer); + + /* Copy (and scale) a region from one to another framebuffer */ + GE( glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, src_fbo->gl_handle) ); + GE( glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, dst_fbo->gl_handle) ); + GE( glBlitFramebufferEXT (src_x, src_y, src_x + src_w, src_y + src_h, + dst_x, dst_y, dst_x + dst_w, dst_y + dst_h, + GL_COLOR_BUFFER_BIT, GL_LINEAR) ); +} + +void +cogl_offscreen_blit (CoglHandle src_buffer, + CoglHandle dst_buffer) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT)) + return; + + CoglFbo *src_fbo; + CoglFbo *dst_fbo; + + /* Make sure these are valid fbo handles */ + if (!cogl_is_offscreen_buffer (src_buffer)) + return; + + if (!cogl_is_offscreen_buffer (dst_buffer)) + return; + + src_fbo = _cogl_fbo_pointer_from_handle (src_buffer); + dst_fbo = _cogl_fbo_pointer_from_handle (dst_buffer); + + /* Copy (and scale) whole image from one to another framebuffer */ + GE( glBindFramebufferEXT (GL_READ_FRAMEBUFFER_EXT, src_fbo->gl_handle) ); + GE( glBindFramebufferEXT (GL_DRAW_FRAMEBUFFER_EXT, dst_fbo->gl_handle) ); + GE( glBlitFramebufferEXT (0, 0, src_fbo->width, src_fbo->height, + 0, 0, dst_fbo->width, dst_fbo->height, + GL_COLOR_BUFFER_BIT, GL_LINEAR) ); +} + +void +cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglFbo *fbo = NULL; + + if (target == COGL_OFFSCREEN_BUFFER) + { + /* Make sure it is a valid fbo handle */ + if (!cogl_is_offscreen_buffer (offscreen)) + return; + + fbo = _cogl_fbo_pointer_from_handle (offscreen); + + /* Check current draw buffer target */ + if (ctx->draw_buffer != COGL_OFFSCREEN_BUFFER) + { + /* Push the viewport and matrix setup if redirecting + from a non-screen buffer */ + GE( glPushAttrib (GL_VIEWPORT_BIT) ); + + GE( glMatrixMode (GL_PROJECTION) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + } + else + { + /* Override viewport and matrix setup if redirecting + from another offscreen buffer */ + GE( glMatrixMode (GL_PROJECTION) ); + GE( glLoadIdentity () ); + + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glLoadIdentity () ); + } + + /* Setup new viewport and matrices */ + GE( glViewport (0, 0, fbo->width, fbo->height) ); + GE( glTranslatef (-1.0f, -1.0f, 0.0f) ); + GE( glScalef (2.0f / fbo->width, 2.0f / fbo->height, 1.0f) ); + + /* Bind offscreen framebuffer object */ + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbo->gl_handle) ); + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + /* Some implementation require a clear before drawing + to an fbo. Luckily it is affected by scissor test. */ + /* FIXME: test where exactly this is needed end whether + a glClear with 0 argument is enough */ + GE( glPushAttrib (GL_SCISSOR_BIT) ); + GE( glScissor (0,0,0,0) ); + GE( glEnable (GL_SCISSOR_TEST) ); + GE( glClear (GL_COLOR_BUFFER_BIT) ); + GE( glPopAttrib () ); + + } + else if ((target & COGL_WINDOW_BUFFER) || + (target & COGL_MASK_BUFFER)) + { + /* Check current draw buffer target */ + if (ctx->draw_buffer == COGL_OFFSCREEN_BUFFER) + { + /* Pop viewport and matrices if redirecting back + from an offscreen buffer */ + GE( glPopAttrib () ); + + GE( glMatrixMode (GL_PROJECTION) ); + GE( glPopMatrix () ); + + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glPopMatrix () ); + } + + /* Bind window framebuffer object */ + GE( glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0) ); + + + if (target == COGL_WINDOW_BUFFER) + { + /* Draw to RGB channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE) ); + } + else if (target == COGL_MASK_BUFFER) + { + /* Draw only to ALPHA channel */ + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) ); + } + else + { + /* Draw to all channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + } + } + + /* Store new target */ + ctx->draw_buffer = target; +} diff --git a/clutter/cogl/gl/cogl-fbo.h b/clutter/cogl/gl/cogl-fbo.h new file mode 100644 index 000000000..f2dd5298f --- /dev/null +++ b/clutter/cogl/gl/cogl-fbo.h @@ -0,0 +1,38 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_FBO_H +#define __COGL_FBO_H + +typedef struct +{ + guint ref_count; + int width; + int height; + GLuint gl_handle; + +} CoglFbo; + +#endif /* __COGL_FBO_H */ diff --git a/clutter/cogl/gl/cogl-internal.h b/clutter/cogl/gl/cogl-internal.h new file mode 100644 index 000000000..1f6b8639e --- /dev/null +++ b/clutter/cogl/gl/cogl-internal.h @@ -0,0 +1,61 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_INTERNAL_H +#define __COGL_INTERNAL_H + +#define COGL_DEBUG 0 + +#if COGL_DEBUG +#define GE(x...) G_STMT_START { \ + GLenum err; \ + (x); \ + while ((err = glGetError()) != GL_NO_ERROR) { \ + fprintf(stderr, "glError: %s caught at %s:%u\n", \ + (char *)error_string(err), \ + __FILE__, __LINE__); \ + } \ +} G_STMT_END +#else +#define GE(x) (x); +#endif + +#define COGL_ENABLE_BLEND (1<<1) +#define COGL_ENABLE_TEXTURE_2D (1<<2) +#define COGL_ENABLE_ALPHA_TEST (1<<3) +#define COGL_ENABLE_TEXTURE_RECT (1<<4) +#define COGL_ENABLE_VERTEX_ARRAY (1<<5) +#define COGL_ENABLE_TEXCOORD_ARRAY (1<<6) + +gint +_cogl_get_format_bpp (CoglPixelFormat format); + +void +cogl_enable (gulong flags); + +gulong +cogl_get_enable (); + +#endif /* __COGL_INTERNAL_H */ diff --git a/clutter/cogl/gl/cogl-primitives.c b/clutter/cogl/gl/cogl-primitives.c new file mode 100644 index 000000000..352ad46d3 --- /dev/null +++ b/clutter/cogl/gl/cogl-primitives.c @@ -0,0 +1,1092 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-context.h" + +#include +#include + +#define _COGL_MAX_BEZ_RECURSE_DEPTH 16 + +/** + * SECTION:cogl-primitives + * @short_description: Functions that draw various primitive shapes and + * allow for construction of more complex paths. + * + * There are three levels on which drawing with cogl can be used. The + * highest level functions construct various simple primitive shapes + * to be either filled or stroked. Using a lower-level set of functions + * more complex and arbitrary paths can be constructed by concatenating + * straight line, bezier curve and arc segments. Additionally there + * are utility functions that draw the most common primitives - rectangles + * and trapezoids - in a maximaly optimized fashion. + * + * When constructing arbitrary paths, the current pen location is + * initialized using the move_to command. The subsequent path segments + * implicitly use the last pen location as their first vertex and move + * the pen location to the last vertex they produce at the end. Also + * there are special versions of functions that allow specifying the + * vertices of the path segments relative to the last pen location + * rather then in the absolute coordinates. + */ + +/** + * cogl_fast_fill_rectangle: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * + * Fills a rectangle at the given coordinates with the current + * drawing color in a highly optimizied fashion. + **/ +void +cogl_fast_fill_rectangle (gint x, + gint y, + guint width, + guint height) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0); + + GE( glRecti (x, y, x + width, y + height) ); +} + +/** + * cogl_fast_fill_rectanglex: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * + * A fixed-point version of cogl_fast_fill_rectangle. + **/ +void +cogl_fast_fill_rectanglex (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0); + + GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x), + CLUTTER_FIXED_TO_FLOAT (y), + CLUTTER_FIXED_TO_FLOAT (x + width), + CLUTTER_FIXED_TO_FLOAT (y + height)) ); +} + +/** + * cogl_fast_fill_trapezoid: + * @y1: Y coordinate of the top two vertices. + * @x11: X coordinate of the top-left vertex. + * @x21: X coordinate of the top-right vertex. + * @y2: Y coordinate of the bottom two vertices. + * @x12: X coordinate of the bottom-left vertex. + * @x22: X coordinate of the bottom-right vertex. + * + * Fills a trapezoid at the given coordinates with the current + * drawing color in a highly optimized fashion. + **/ +void +cogl_fast_fill_trapezoid (gint y1, + gint x11, + gint x21, + gint y2, + gint x12, + gint x22) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0); + + GE( glBegin (GL_QUADS) ); + GE( glVertex2i (x11, y1) ); + GE( glVertex2i (x21, y1) ); + GE( glVertex2i (x22, y2) ); + GE( glVertex2i (x12, y2) ); + GE( glEnd () ); +} + +/** + * cogl_fast_fill_trapezoidx: + * @y1: Y coordinate of the top two vertices. + * @x11: X coordinate of the top-left vertex. + * @x21: X coordinate of the top-right vertex. + * @y2: Y coordinate of the bottom two vertices. + * @x12: X coordinate of the bottom-left vertex. + * @x22: X coordinate of the bottom-right vertex. + * + * A fixed-point version of cogl_fast_fill_trapezoid. + **/ +void +cogl_fast_fill_trapezoidx (ClutterFixed y1, + ClutterFixed x11, + ClutterFixed x21, + ClutterFixed y2, + ClutterFixed x12, + ClutterFixed x22) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0); + + GE( glBegin (GL_QUADS) ); + + GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x11), + CLUTTER_FIXED_TO_FLOAT (y1)) ); + GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x21), + CLUTTER_FIXED_TO_FLOAT (y1)) ); + GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x22), + CLUTTER_FIXED_TO_FLOAT (y2)) ); + GE( glVertex2f (CLUTTER_FIXED_TO_FLOAT (x12), + CLUTTER_FIXED_TO_FLOAT (y2)) ); + GE( glEnd () ); +} + +static void +_cogl_path_clear_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes) + g_free(ctx->path_nodes); + + ctx->path_nodes = (CoglFloatVec2*) g_malloc (2 * sizeof(CoglFloatVec2)); + ctx->path_nodes_size = 0; + ctx->path_nodes_cap = 2; +} + +static void +_cogl_path_add_node (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglFloatVec2 *new_nodes = NULL; + + if (ctx->path_nodes_size == ctx->path_nodes_cap) + { + new_nodes = g_realloc (ctx->path_nodes, + 2 * ctx->path_nodes_cap + * sizeof (CoglFloatVec2)); + + if (new_nodes == NULL) return; + + ctx->path_nodes = new_nodes; + ctx->path_nodes_cap *= 2; + } + + ctx->path_nodes [ctx->path_nodes_size] .x = CLUTTER_FIXED_TO_FLOAT (x); + ctx->path_nodes [ctx->path_nodes_size] .y = CLUTTER_FIXED_TO_FLOAT (y); + ctx->path_nodes_size++; + + if (ctx->path_nodes_size == 1) + { + ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; + ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; + } + else + { + if (x < ctx->path_nodes_min.x) ctx->path_nodes_min.x = x; + if (x > ctx->path_nodes_max.x) ctx->path_nodes_max.x = x; + if (y < ctx->path_nodes_min.y) ctx->path_nodes_min.y = y; + if (y > ctx->path_nodes_max.y) ctx->path_nodes_max.y = y; + } +} + +static void +_cogl_path_stroke_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) ); + GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); +} + +static void +_cogl_path_fill_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + guint bounds_x; + guint bounds_y; + guint bounds_w; + guint bounds_h; + + GE( glClear (GL_STENCIL_BUFFER_BIT) ); + + GE( glEnable (GL_STENCIL_TEST) ); + GE( glStencilFunc (GL_ALWAYS, 0x0, 0x0) ); + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) ); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FLOAT, 0, ctx->path_nodes) ); + GE( glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) ); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) ); + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + bounds_x = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.x); + bounds_y = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.y); + bounds_w = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x); + bounds_h = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y); + + cogl_fast_fill_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); + + GE( glDisable (GL_STENCIL_TEST) ); +} + +/** + * cogl_fill: + * + * Fills the constructed shape using the current drawing color. + **/ +void +cogl_fill () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes_size == 0) + return; + + _cogl_path_fill_nodes(); + +} + +/** + * cogl_stroke: + * + * Strokes the constructed shape using the current drawing color + * and a width of 1 pixel (regardless of the current transformation + * matrix). + **/ +void +cogl_stroke () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes_size == 0) + return; + + _cogl_path_stroke_nodes(); +} + +/** + * cogl_path_move_to: + * @x: X coordinate of the pen location to move to. + * @y: Y coordinate of the pen location to move to. + * + * Clears the previously constructed shape and begins a new path + * contour by moving the pen to the given coordinates. + **/ +void +cogl_path_move_to (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* FIXME: handle multiple contours maybe? */ + + _cogl_path_clear_nodes (); + _cogl_path_add_node (x, y); + + ctx->path_start.x = x; + ctx->path_start.y = y; + + ctx->path_pen = ctx->path_start; +} + +/** + * cogl_path_move_to_rel: + * @x: X offset from the current pen location to move the pen to. + * @y: Y offset from the current pen location to move the pen to. + * + * Clears the previously constructed shape and begins a new path + * contour by moving the pen to the given coordinates relative + * to the current pen location. + **/ +void +cogl_path_move_to_rel (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_move_to (ctx->path_pen.x + x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_line_to: + * @x: X coordinate of the end line vertex + * @y: Y coordinate of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates. + **/ +void +cogl_path_line_to (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_path_add_node (x, y); + + ctx->path_pen.x = x; + ctx->path_pen.y = y; +} + +/** + * cogl_path_line_to: + * @x: X offset from the current pen location of the end line vertex + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates relative to the current pen location. + **/ +void +cogl_path_line_to_rel (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x + x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_h_line_to: + * @x: X coordinate of the end line vertex + * + * Adds a straight horizontal line segment to the current path that + * ends at the given X coordinate and current pen Y coordinate. + **/ +void +cogl_path_h_line_to (ClutterFixed x) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (x, + ctx->path_pen.y); +} + +/** + * cogl_path_v_line_to: + * @y: Y coordinate of the end line vertex + * + * Adds a stright vertical line segment to the current path that ends + * at the current pen X coordinate and the given Y coordinate. + **/ +void +cogl_path_v_line_to (ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x, + y); +} + +/** + * cogl_path_h_line_to_rel: + * @x: X offset from the current pen location of the end line vertex + * + * Adds a straight horizontal line segment to the current path that + * ends at the given X coordinate relative to the current pen location + * and current pen Y coordinate. + **/ +void +cogl_path_h_line_to_rel (ClutterFixed x) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x + x, + ctx->path_pen.y); +} + +/** + * cogl_path_v_line_to_rel: + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a stright vertical line segment to the current path that ends + * at the current pen X coordinate and the given Y coordinate relative + * to the current pen location. + **/ +void +cogl_path_v_line_to_rel (ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_close: + * + * Closes the path being constructed by adding a straight line segment + * to it that ends at the first vertex of the path. + **/ +void +cogl_path_close () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_path_add_node (ctx->path_start.x, ctx->path_start.y); + ctx->path_pen = ctx->path_start; +} + +/** + * cogl_line: + * @x1: X coordinate of the start line vertex + * @y1: Y coordinate of the start line vertex + * @x2: X coordinate of the end line vertex + * @y2: Y coordinate of the end line vertex + * + * Clears the previously constructed shape and constructs a straight + * line shape start and ending at the given coordinates. + **/ +void +cogl_line (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + cogl_path_move_to (x1, y1); + cogl_path_line_to (x2, y2); +} + +/** + * cogl_polyline: + * @coords: A pointer to the first element of an array of fixed-point + * values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Clears the previously constructed shape and constructs a series of straight + * line segments, starting from the first given vertex coordinate. Each + * subsequent segment stars where the previous one ended and ends at the next + * given vertex coordinate. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. (num_points - 1) segments will + * be constructed. + **/ +void +cogl_polyline (ClutterFixed *coords, + gint num_points) +{ + gint c = 0; + + cogl_path_move_to (coords[0], coords[1]); + + for (c = 1; c < num_points; ++c) + cogl_path_line_to (coords[2*c], coords[2*c+1]); +} + + +/** + * cogl_polygon: + * @coords: A pointer to the first element of an array of fixed-point + * values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Clears the previously constructed shape and constructs a polygonal + * shape of the given number of vertices. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. + **/ +void +cogl_polygon (ClutterFixed *coords, + gint num_points) +{ + cogl_polyline (coords, num_points); + cogl_path_close (); +} + +/** + * cogl_rectangle: + * @x: X coordinate of the top-left corner. + * @y: Y coordinate of the top-left corner. + * @width: Rectangle width. + * @height: Rectangle height. + * + * Clears the previously constructed shape and constructs a rectangular + * shape at the given coordinates. + **/ +void +cogl_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height) +{ + cogl_path_move_to (x, y); + cogl_path_line_to (x + width, y); + cogl_path_line_to (x + width, y + height); + cogl_path_line_to (x, y + height); + cogl_path_close (); +} + +static void +_cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step, + guint move_first) +{ + ClutterAngle a = 0x0; + ClutterAngle temp = 0x0; + ClutterFixed cosa = 0x0; + ClutterFixed sina = 0x0; + ClutterFixed px = 0x0; + ClutterFixed py = 0x0; + + /* Fix invalid angles */ + + if (angle_1 == angle_2 || angle_step == 0x0) + return; + + if (angle_step < 0x0) + angle_step = -angle_step; + + if (angle_2 < angle_1) + { + temp = angle_1; + angle_1 = angle_2; + angle_2 = temp; + } + + /* Walk the arc by given step */ + + for (a = angle_1; a < angle_2; a += angle_step) + { + cosa = clutter_cosi (a); + sina = clutter_sini (a); + + px = center_x + CFX_MUL (cosa, radius_x); + py = center_y + CFX_MUL (sina, radius_y); + + if (a == angle_1 && move_first) + cogl_path_move_to (px, py); + else + cogl_path_line_to (px, py); + } +} + +/** + * cogl_path_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. + **/ +void +cogl_path_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _cogl_arc (center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +/** + * cogl_path_arc_rel: + * @center_x: X offset from the current pen location of the elliptical + * arc center + * @center_y: Y offset from the current pen location of the elliptical + * arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. + **/ +void +cogl_path_arc_rel (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_arc (ctx->path_pen.x + center_x, + ctx->path_pen.y + center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +/** + * cogl_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Clears the previously constructed shape and constructs and elliptical arc + * shape. + **/ +void +cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _cogl_arc (center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 1 /* move first */); +} + +/** + * cogl_ellipse: + * @center_x: X coordinate of the ellipse center + * @center_y: Y coordinate of the ellipse center + * @radius_x: X radius of the ellipse + * @radius_y: Y radius of the ellipse + * @angle_step: Angle increment resolution for subdivision + * + * Clears the previously constructed shape and constructs an ellipse + * shape. + **/ +void +cogl_ellipse (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_step) +{ + + /* FIXME: if shows to be slow might be optimized + * by mirroring just a quarter of it */ + + _cogl_arc (center_x, center_y, + radius_x, radius_y, + 0, CLUTTER_ANGLE_FROM_DEG(360), + angle_step, 1 /* move first */); + + cogl_path_close(); +} + +/** + * cogl_round_rectangle: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * @radius: Radius of the corner arcs. + * @arc_step: Angle increment resolution for subdivision of + * the corner arcs. + * + * Clears the previously constructed shape and constructs a rectangular + * shape with rounded corners. + **/ +void +cogl_round_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height, + ClutterFixed radius, + ClutterAngle arc_step) +{ + ClutterFixed inner_width = width - (radius << 1); + ClutterFixed inner_height = height - (radius << 1); + + cogl_path_move_to (x, y + radius); + cogl_path_arc_rel (radius, 0, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (180), + CLUTTER_ANGLE_FROM_DEG (270), + arc_step); + + cogl_path_h_line_to_rel (inner_width); + cogl_path_arc_rel (0, radius, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (-90), + CLUTTER_ANGLE_FROM_DEG (0), + arc_step); + + cogl_path_v_line_to_rel (inner_height); + cogl_path_arc_rel (-radius, 0, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (0), + CLUTTER_ANGLE_FROM_DEG (90), + arc_step); + + cogl_path_h_line_to_rel (-inner_width); + cogl_path_arc_rel (0, -radius, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (90), + CLUTTER_ANGLE_FROM_DEG (180), + arc_step); + + cogl_path_close (); +} + +static void +_cogl_path_bezier2_sub (CoglBezQuad *quad) +{ + CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezQuad *qleft; + CoglBezQuad *qright; + CoglBezQuad *q; + CoglFixedVec2 mid; + CoglFixedVec2 dif; + CoglFixedVec2 c1; + CoglFixedVec2 c2; + CoglFixedVec2 c3; + gint qindex; + + /* Put first curve on stack */ + quads[0] = *quad; + qindex = 0; + + /* While stack is not empty */ + while (qindex >= 0) + { + + q = &quads[qindex]; + + /* Calculate distance of control point from its + * counterpart on the line between end points */ + mid.x = ((q->p1.x + q->p3.x) >> 1); + mid.y = ((q->p1.y + q->p3.y) >> 1); + dif.x = (q->p2.x - mid.x); + dif.y = (q->p2.y - mid.y); + if (dif.x < 0) dif.x = -dif.x; + if (dif.y < 0) dif.y = -dif.y; + + /* Cancel if the curve is flat enough */ + if (dif.x + dif.y <= CFX_ONE + || qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1) + { + /* Add subdivision point (skip last) */ + if (qindex == 0) return; + _cogl_path_add_node (q->p3.x, q->p3.y); + --qindex; continue; + } + + /* Left recursion goes on top of stack! */ + qright = q; qleft = &quads[++qindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((q->p1.x + q->p2.x) >> 1); + c1.y = ((q->p1.y + q->p2.y) >> 1); + c3.x = ((q->p2.x + q->p3.x) >> 1); + c3.y = ((q->p2.y + q->p3.y) >> 1); + c2.x = ((c1.x + c3.x) >> 1); + c2.y = ((c1.y + c3.y) >> 1); + + /* Add left recursion onto stack */ + qleft->p1 = q->p1; + qleft->p2 = c1; + qleft->p3 = c2; + + /* Add right recursion onto stack */ + qright->p1 = c2; + qright->p2 = c3; + qright->p3 = q->p3; + } +} + +static void +_cogl_path_bezier3_sub (CoglBezCubic *cubic) +{ + CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezCubic *cleft; + CoglBezCubic *cright; + CoglBezCubic *c; + CoglFixedVec2 dif1; + CoglFixedVec2 dif2; + CoglFixedVec2 mm; + CoglFixedVec2 c1; + CoglFixedVec2 c2; + CoglFixedVec2 c3; + CoglFixedVec2 c4; + CoglFixedVec2 c5; + gint cindex; + + /* Put first curve on stack */ + cubics[0] = *cubic; + cindex = 0; + + while (cindex >= 0) + { + c = &cubics[cindex]; + +#define CFX_MUL2(x) ((x) << 1) +#define CFX_MUL3(x) (((x) << 1) + (x)) +#define CFX_SQ(x) CFX_MUL (x, x) + + /* Calculate distance of control points from their + * counterparts on the line between end points */ + dif1.x = CFX_MUL3 (c->p2.x) - CFX_MUL2 (c->p1.x) - c->p4.x; + dif1.y = CFX_MUL3 (c->p2.y) - CFX_MUL2 (c->p1.y) - c->p4.y; + dif2.x = CFX_MUL3 (c->p3.x) - CFX_MUL2 (c->p4.x) - c->p1.x; + dif2.y = CFX_MUL3 (c->p3.y) - CFX_MUL2 (c->p4.y) - c->p1.y; + if (dif1.x < 0) dif1.x = -dif1.x; + if (dif1.y < 0) dif1.y = -dif1.y; + if (dif2.x < 0) dif2.x = -dif2.x; + if (dif2.y < 0) dif2.y = -dif2.y; + +#undef CFX_MUL2 +#undef CFX_MUL3 +#undef CFX_SQ + + /* Pick the greatest of two distances */ + if (dif1.x < dif2.x) dif1.x = dif2.x; + if (dif1.y < dif2.y) dif1.y = dif2.y; + + /* Cancel if the curve is flat enough */ + if (dif1.x + dif1.y <= CFX_ONE + || cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1) + { + /* Add subdivision point (skip last) */ + if (cindex == 0) return; + _cogl_path_add_node (c->p4.x, c->p4.y); + --cindex; continue; + } + + /* Left recursion goes on top of stack! */ + cright = c; cleft = &cubics[++cindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((c->p1.x + c->p2.x) >> 1); + c1.y = ((c->p1.y + c->p2.y) >> 1); + mm.x = ((c->p2.x + c->p3.x) >> 1); + mm.y = ((c->p2.y + c->p3.y) >> 1); + c5.x = ((c->p3.x + c->p4.x) >> 1); + c5.y = ((c->p3.y + c->p4.y) >> 1); + + c2.x = ((c1.x + mm.x) >> 1); + c2.y = ((c1.y + mm.y) >> 1); + c4.x = ((mm.x + c5.x) >> 1); + c4.y = ((mm.y + c5.y) >> 1); + + c3.x = ((c2.x + c4.x) >> 1); + c3.y = ((c2.y + c4.y) >> 1); + + /* Add left recursion to stack */ + cleft->p1 = c->p1; + cleft->p2 = c1; + cleft->p3 = c2; + cleft->p4 = c3; + + /* Add right recursion to stack */ + cright->p1 = c3; + cright->p2 = c4; + cright->p3 = c5; + cright->p4 = c->p4; + } +} + +/** + * cogl_path_bezier2_to: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * + * Adds a quadratic bezier curve segment to the current path with the given + * second and third control points and using current pen location as the + * first control point. + **/ +void +cogl_path_bezier2_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglBezQuad quad; + + /* Prepare quadratic curve */ + quad.p1 = ctx->path_pen; + quad.p2.x = x1; + quad.p2.y = y1; + quad.p3.x = x2; + quad.p3.y = y2; + + /* Run subdivision */ + _cogl_path_bezier2_sub (&quad); + + /* Add last point */ + _cogl_path_add_node (quad.p3.x, quad.p3.y); + ctx->path_pen = quad.p3; +} + +/** + * cogl_path_bezier3_to: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * @x3: X coordinate of the fourth bezier control point + * @y3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. + **/ +void +cogl_path_bezier3_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglBezCubic cubic; + + /* Prepare cubic curve */ + cubic.p1 = ctx->path_pen; + cubic.p2.x = x1; + cubic.p2.y = y1; + cubic.p3.x = x2; + cubic.p3.y = y2; + cubic.p4.x = x3; + cubic.p4.y = y3; + + /* Run subdivision */ + _cogl_path_bezier3_sub (&cubic); + + /* Add last point */ + _cogl_path_add_node (cubic.p4.x, cubic.p4.y); + ctx->path_pen = cubic.p4; +} + +/** + * cogl_path_bezier2_to_rel: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * + * Adds a quadratic bezier curve segment to the current path with the given + * second and third control points and using current pen location as the + * first control point. The given coordinates are relative to the current + * pen location. + **/ +void +cogl_path_bezier2_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_bezier2_to (ctx->path_pen.x + x1, + ctx->path_pen.y + y2, + ctx->path_pen.x + x2, + ctx->path_pen.y + y2); +} + +/** + * cogl_path_bezier3_to_rel: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * @x3: X coordinate of the fourth bezier control point + * @y3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. The given coordinates are relative to the + * current pen location. + **/ +void +cogl_path_bezier3_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_bezier3_to (ctx->path_pen.x + x1, + ctx->path_pen.y + y2, + ctx->path_pen.x + x2, + ctx->path_pen.y + y2, + ctx->path_pen.x + x3, + ctx->path_pen.y + y3); +} diff --git a/clutter/cogl/gl/cogl-primitives.h b/clutter/cogl/gl/cogl-primitives.h new file mode 100644 index 000000000..f43ab6a47 --- /dev/null +++ b/clutter/cogl/gl/cogl-primitives.h @@ -0,0 +1,61 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_PRIMITIVES_H +#define __COGL_PRIMITIVES_H + +typedef struct _CoglFixedVec2 CoglFixedVec2; +typedef struct _CoglFloatVec2 CoglFloatVec2; +typedef struct _CoglBezQuad CoglBezQuad; +typedef struct _CoglBezCubic CoglBezCubic; + +struct _CoglFixedVec2 +{ + ClutterFixed x; + ClutterFixed y; +}; + +struct _CoglFloatVec2 +{ + GLfloat x; + GLfloat y; +}; + +struct _CoglBezQuad +{ + CoglFixedVec2 p1; + CoglFixedVec2 p2; + CoglFixedVec2 p3; +}; + +struct _CoglBezCubic +{ + CoglFixedVec2 p1; + CoglFixedVec2 p2; + CoglFixedVec2 p3; + CoglFixedVec2 p4; +}; + +#endif /* __COGL_PRIMITIVES_H */ diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c new file mode 100644 index 000000000..5e4d6ee54 --- /dev/null +++ b/clutter/cogl/gl/cogl-texture.c @@ -0,0 +1,2303 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-texture.h" +#include "cogl-context.h" + +#include +#include + +/* +#define COGL_DEBUG 1 + +#define GE(x) \ +{ \ + glGetError(); x; \ + GLuint err = glGetError(); \ + if (err != 0) \ + printf("err: 0x%x\n", err); \ +} */ + +struct _CoglSpanIter +{ + gint index; + GArray *array; + CoglTexSliceSpan *span; + ClutterFixed pos; + ClutterFixed next_pos; + ClutterFixed origin; + ClutterFixed cover_start; + ClutterFixed cover_end; + ClutterFixed intersect_start; + ClutterFixed intersect_end; + ClutterFixed intersect_start_local; + ClutterFixed intersect_end_local; + gboolean intersects; +}; + +/* + * _cogl_texture_handle_find: + * @handle: A texture handle + * + * Returns the index of the given CoglHandle if found in the + * handle array. + */ +static gint +_cogl_texture_handle_find (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, -1); + + gint i; + + if (ctx->texture_handles == NULL) + return -1; + + for (i=0; i < ctx->texture_handles->len; ++i) + if (g_array_index (ctx->texture_handles, CoglHandle, i) == handle) + return i; + + return -1; +} + +/* + * _cogl_texture_handle_new: + * @tex: A pointer to an allocated CoglTexture structure + * + * Returns a new CoglHandle for the given CoglTexture + * object. + */ +static CoglHandle +_cogl_texture_handle_new (CoglTexture *tex) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + CoglHandle handle = (CoglHandle)tex; + + if (ctx->texture_handles == NULL) + ctx->texture_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); + + g_array_append_val (ctx->texture_handles, handle); + + return handle; +} + +/* + * _cogl_texture_handle_release: + * @handle: A valid CoglHandle + * + * Frees the given CoglHandle for use with another object. + */ +static void +_cogl_texture_handle_release (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + gint i; + + if ( (i = _cogl_texture_handle_find (handle)) == -1) + return; + + g_array_remove_index_fast (ctx->texture_handles, i); +} + +/* + * _cogl_texture_pointer_from_handle: + * @handle: A valid CoglHandle + * + * Returns a pointer to the texture object referenced by + * given handle. + */ +CoglTexture * +_cogl_texture_pointer_from_handle (CoglHandle handle) +{ + return (CoglTexture*) handle; +} + +gboolean +cogl_is_texture (CoglHandle handle) +{ + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return _cogl_texture_handle_find (handle) >= 0; +} + +static void +_cogl_texture_bitmap_free (CoglTexture *tex) +{ + if (tex->bitmap.data != NULL && tex->bitmap_owner) + g_free (tex->bitmap.data); + + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; +} + +static void +_cogl_texture_bitmap_swap (CoglTexture *tex, + CoglBitmap *new_bitmap) +{ + if (tex->bitmap.data != NULL && tex->bitmap_owner) + g_free (tex->bitmap.data); + + tex->bitmap = *new_bitmap; + tex->bitmap_owner = TRUE; +} + +static void +_cogl_span_iter_update (CoglSpanIter *iter) +{ + /* Pick current span */ + iter->span = &g_array_index (iter->array, + CoglTexSliceSpan, + iter->index); + + /* Offset next position by span size */ + iter->next_pos = iter->pos + + CLUTTER_INT_TO_FIXED (iter->span->size - iter->span->waste); + + /* Check if span intersects the area to cover */ + if (iter->next_pos <= iter->cover_start || + iter->pos >= iter->cover_end) + { + /* Intersection undefined */ + iter->intersects = FALSE; + return; + } + + iter->intersects = TRUE; + + /* Clip start position to coverage area */ + if (iter->pos < iter->cover_start) + iter->intersect_start = iter->cover_start; + else + iter->intersect_start = iter->pos; + + /* Clip end position to coverage area */ + if (iter->next_pos > iter->cover_end) + iter->intersect_end = iter->cover_end; + else + iter->intersect_end = iter->next_pos; +} + +static void +_cogl_span_iter_begin (CoglSpanIter *iter, + GArray *array, + ClutterFixed origin, + ClutterFixed cover_start, + ClutterFixed cover_end) +{ + /* Copy info */ + iter->index = 0; + iter->array = array; + iter->span = NULL; + iter->origin = origin; + iter->cover_start = cover_start; + iter->cover_end = cover_end; + iter->pos = iter->origin; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +void +_cogl_span_iter_next (CoglSpanIter *iter) +{ + /* Move current position */ + iter->pos = iter->next_pos; + + /* Pick next slice (wrap when last reached) */ + iter->index = (iter->index + 1) % iter->array->len; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +static gboolean +_cogl_span_iter_end (CoglSpanIter *iter) +{ + /* End reached when whole area covered */ + return iter->pos >= iter->cover_end; +} + +static void +_cogl_subregion_gl_store_rules (gint bmp_rowstride, + gint bmp_width, + gint bmp_bpp, + gint src_x, + gint src_y, + gboolean pack) +{ + const GLenum ALIGNMENT = pack ? + GL_PACK_ALIGNMENT : GL_UNPACK_ALIGNMENT; + + const GLenum ROW_LENGTH = pack ? + GL_PACK_ROW_LENGTH : GL_UNPACK_ROW_LENGTH; + + const GLenum SKIP_ROWS = pack ? + GL_PACK_SKIP_ROWS : GL_UNPACK_SKIP_ROWS; + + const GLenum SKIP_PIXELS = pack ? + GL_PACK_SKIP_PIXELS : GL_UNPACK_SKIP_PIXELS; + + /* Encode the part of the rowstride that is a multiple of bmp_bpp in + ROW_LENGTH and the remainder in ALIGNMENT */ + GE( glPixelStorei (ROW_LENGTH, bmp_rowstride / bmp_bpp) ); + + if (bmp_rowstride == bmp_width * bmp_bpp) + { + GE( glPixelStorei (ALIGNMENT, 1) ); + } + else + { + if ((bmp_rowstride % 4) == 0) + { + GE( glPixelStorei (ALIGNMENT, 4) ); + } + else + { + if ((bmp_rowstride % 2) == 0) + GE( glPixelStorei (ALIGNMENT, 2) ); + } + } + + GE( glPixelStorei (SKIP_ROWS, src_y) ); + GE( glPixelStorei (SKIP_PIXELS, src_x) ); +} + +static gboolean +_cogl_texture_upload_to_gl (CoglTexture *tex) +{ + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + gint bpp; + gint x,y; + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Iterate vertical slices */ + for (y = 0; y < tex->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex->slice_x_spans->len; ++x) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + /* Pick the gl texture object handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y * tex->slice_x_spans->len + x); + + /* Setup gl alignment to match rowstride and top-left corner */ + _cogl_subregion_gl_store_rules (tex->bitmap.rowstride, + tex->bitmap.width, + bpp, + x_span->start, + y_span->start, + FALSE); + + /* Upload new image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glTexSubImage2D (tex->gl_target, 0, 0, 0, + x_span->size - x_span->waste, + y_span->size - y_span->waste, + tex->gl_format, tex->gl_type, + tex->bitmap.data) ); + } + } + + return TRUE; +} + +static gboolean +_cogl_texture_download_from_gl (CoglTexture *tex, + CoglBitmap *target_bmp, + GLuint target_gl_format, + GLuint target_gl_type) +{ + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + gint bpp; + gint x,y; + CoglBitmap slice_bmp; + + bpp = _cogl_get_format_bpp (target_bmp->format); + + /* Iterate vertical slices */ + for (y = 0; y < tex->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex->slice_x_spans->len; ++x) + { + /*if (x != 0 || y != 1) continue;*/ + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + /* Pick the gl texture object handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y * tex->slice_x_spans->len + x); + + /* If there's any waste we need to copy manually + (no glGetTexSubImage) */ + + if (y_span->waste != 0 || x_span->waste != 0) + { + /* Setup temp bitmap for slice subregion */ + slice_bmp.format = tex->bitmap.format; + slice_bmp.width = x_span->size; + slice_bmp.height = y_span->size; + slice_bmp.rowstride = bpp * slice_bmp.width; + slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride * + slice_bmp.height); + + /* Setup gl alignment to 0,0 top-left corner */ + _cogl_subregion_gl_store_rules (slice_bmp.rowstride, + slice_bmp.width, + bpp, 0, 0, TRUE); + + /* Download slice image data into temp bmp */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE (glGetTexImage (tex->gl_target, 0, + target_gl_format, + target_gl_type, + slice_bmp.data) ); + + /* Copy portion of slice from temp to target bmp */ + _cogl_bitmap_copy_subregion (&slice_bmp, + target_bmp, + 0, 0, + x_span->start, + y_span->start, + x_span->size - x_span->waste, + y_span->size - y_span->waste); + /* Free temp bitmap */ + g_free (slice_bmp.data); + } + else + { + /* Setup gl alignment to match rowstride and top-left corner */ + + /* FIXME: for some strange reason any value other than 0 + * for GL_PACK_SKIP_PIXELS or GL_PACK_SKIP_ROWS corrupts the + * memory. As a workaround we offset data pointer manually + + _cogl_subregion_gl_store_rules (target_bmp->rowstride, + target_bmp->width, + bpp, + x_span->start, + y_span->start, + TRUE);*/ + _cogl_subregion_gl_store_rules (target_bmp->rowstride, + target_bmp->width, + bpp, + 0, 0, + TRUE); + + /* Download slice image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glGetTexImage (tex->gl_target, 0, + target_gl_format, + target_gl_type, + target_bmp->data + + x_span->start * bpp + + y_span->start * target_bmp->rowstride) ); + } + } + } + + return TRUE; +} + +static gboolean +_cogl_texture_upload_subregion_to_gl (CoglTexture *tex, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height, + CoglBitmap *source_bmp, + GLuint source_gl_format, + GLuint source_gl_type) +{ + gint bpp; + CoglSpanIter x_iter; + CoglSpanIter y_iter; + GLuint gl_handle; + gint source_x = 0, source_y = 0; + gint inter_w = 0, inter_h = 0; + gint local_x = 0, local_y = 0; + + bpp = _cogl_get_format_bpp (source_bmp->format); + + /* Iterate vertical spans */ + for (source_y = src_y, + _cogl_span_iter_begin (&y_iter, tex->slice_y_spans, + 0, CLUTTER_INT_TO_FIXED (dst_y), + CLUTTER_INT_TO_FIXED (dst_y + height)); + + !_cogl_span_iter_end (&y_iter); + + _cogl_span_iter_next (&y_iter), + source_y += inter_h ) + { + /* Iterate horizontal spans */ + for (source_x = src_x, + _cogl_span_iter_begin (&x_iter, tex->slice_x_spans, + 0, CLUTTER_INT_TO_FIXED (dst_x), + CLUTTER_INT_TO_FIXED (dst_x + width)); + + !_cogl_span_iter_end (&x_iter); + + _cogl_span_iter_next (&x_iter), + source_x += inter_w ) + { + /* Pick intersection width and height */ + inter_w = CLUTTER_FIXED_TO_INT (x_iter.intersect_end - + x_iter.intersect_start); + inter_h = CLUTTER_FIXED_TO_INT (y_iter.intersect_end - + y_iter.intersect_start); + + /* Localize intersection top-left corner to slice*/ + local_x = CLUTTER_FIXED_TO_INT (x_iter.intersect_start - + x_iter.pos); + local_y = CLUTTER_FIXED_TO_INT (y_iter.intersect_start - + y_iter.pos); + + /* Pick slice GL handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y_iter.index * tex->slice_x_spans->len + + x_iter.index); + + /* Setup gl alignment to match rowstride and top-left corner */ + + _cogl_subregion_gl_store_rules (source_bmp->rowstride, + source_bmp->width, + bpp, + source_x, + source_y, + FALSE); + + /* Upload new image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glTexSubImage2D (tex->gl_target, 0, + local_x, local_y, + inter_w, inter_h, + source_gl_format, + source_gl_type, + source_bmp->data) ); + } + } + + return TRUE; +} + +static gint +_cogl_rect_slices_for_size (gint size_to_fill, + gint max_span_size, + gint max_waste, + GArray *out_spans) +{ + gint n_spans = 0; + CoglTexSliceSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Repeat until whole area covered */ + while (size_to_fill >= span.size) + { + /* Add another slice span of same size */ + if (out_spans) g_array_append_val (out_spans, span); + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + + /* Add one last smaller slice span */ + if (size_to_fill > 0) + { + span.size = size_to_fill; + if (out_spans) g_array_append_val (out_spans, span); + n_spans++; + } + + return n_spans; +} + +static gint +_cogl_pot_slices_for_size (gint size_to_fill, + gint max_span_size, + gint max_waste, + GArray *out_spans) +{ + gint n_spans = 0; + CoglTexSliceSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Fix invalid max_waste */ + if (max_waste < 0) max_waste = 0; + + while (TRUE) + { + /* Is the whole area covered? */ + if (size_to_fill > span.size) + { + /* Not yet - add a span of this size */ + if (out_spans) g_array_append_val (out_spans, span); + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + else if (span.size - size_to_fill <= max_waste) + { + /* Yes and waste is small enough */ + span.waste = span.size - size_to_fill; + if (out_spans) g_array_append_val (out_spans, span); + return ++n_spans; + } + else + { + /* Yes but waste is too large */ + while (span.size - size_to_fill > max_waste) + { + span.size /= 2; + g_assert (span.size > 0); + } + } + } + + /* Can't get here */ + return 0; +} + +static gboolean +_cogl_texture_size_supported (GLenum gl_target, + GLenum gl_format, + GLenum gl_type, + int width, + int height) +{ + if (gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + /* There is no proxy rectangle texture target so best we can + * do is to check against the safest value (although depending + * on our specific format and type the size could be supported + * when it seems it is not) */ + + GLint max_size = 0; + + GE( glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &max_size) ); + + return (max_size && width <= max_size && height <= max_size); + } + else if (gl_target == GL_TEXTURE_2D) + { + /* Proxy texture allows for a quick check for supported size */ + + GLint new_width = 0; + + GE( glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA, + width, height, 0 /* border */, + gl_format, gl_type, NULL) ); + + GE( glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, + GL_TEXTURE_WIDTH, &new_width) ); + + return new_width != 0; + } + else + { + /* not used */ + return 0; + } +} + +static gboolean +_cogl_texture_slices_create (CoglTexture *tex) +{ + gint bpp; + gint max_width; + gint max_height; + GLuint *gl_handles; + gint n_x_slices; + gint n_y_slices; + gint n_slices; + gint x, y; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + const GLfloat transparent_color[4] = { 0x00, 0x00, 0x00, 0x00 }; + + gint (*slices_for_size) (gint, gint, gint, GArray*); + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Initialize size of largest slice according to supported features*/ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)) + { + max_width = tex->bitmap.width; + max_height = tex->bitmap.height; + tex->gl_target = GL_TEXTURE_2D; + slices_for_size = _cogl_rect_slices_for_size; + } + else if (cogl_features_available (COGL_FEATURE_TEXTURE_RECTANGLE)) + { + max_width = tex->bitmap.width; + max_height = tex->bitmap.height; + tex->gl_target = GL_TEXTURE_RECTANGLE_ARB; + slices_for_size = _cogl_rect_slices_for_size; + } + else + { + max_width = cogl_util_next_p2 (tex->bitmap.width); + max_height = cogl_util_next_p2 (tex->bitmap.height); + tex->gl_target = GL_TEXTURE_2D; + slices_for_size = _cogl_pot_slices_for_size; + } + + /* Negative number means no slicing forced by the user */ + if (tex->max_waste <= -1) + { + CoglTexSliceSpan span; + + /* Check if size supported else bail out */ + if (!_cogl_texture_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + return FALSE; + } + + n_x_slices = 1; + n_y_slices = 1; + + /* Init span arrays */ + tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + /* Add a single span for width and height */ + span.start = 0; + span.size = max_width; + span.waste = max_width - tex->bitmap.width; + g_array_append_val (tex->slice_x_spans, span); + + span.size = max_height; + span.waste = max_height - tex->bitmap.height; + g_array_append_val (tex->slice_y_spans, span); + } + else + { + /* Decrease the size of largest slice until supported by GL */ + while (!_cogl_texture_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + /* Alternate between width and height */ + if (max_width > max_height) + max_width /= 2; + else + max_height /= 2; + + if (max_width == 0 || max_height == 0) + return FALSE; + } + + /* Determine the slices required to cover the bitmap area */ + n_x_slices = slices_for_size (tex->bitmap.width, + max_width, tex->max_waste, + NULL); + + n_y_slices = slices_for_size (tex->bitmap.height, + max_height, tex->max_waste, + NULL); + + /* Init span arrays with reserved size */ + tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_x_slices); + + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_y_slices); + + /* Fill span arrays with info */ + slices_for_size (tex->bitmap.width, + max_width, tex->max_waste, + tex->slice_x_spans); + + slices_for_size (tex->bitmap.height, + max_height, tex->max_waste, + tex->slice_y_spans); + } + + /* Init and resize GL handle array */ + n_slices = n_x_slices * n_y_slices; + + tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), + n_slices); + + g_array_set_size (tex->slice_gl_handles, n_slices); + + + /* Hardware repeated tiling if supported, else tile in software*/ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && n_slices == 1) + tex->wrap_mode = GL_REPEAT; + else + tex->wrap_mode = GL_CLAMP_TO_EDGE; + + /* Generate a "working set" of GL texture objects + * (some implementations might supported faster + * re-binding between textures inside a set) */ + gl_handles = (GLuint*) tex->slice_gl_handles->data; + + GE( glGenTextures (n_slices, gl_handles) ); + + + /* Init each GL texture object */ + for (y = 0; y < n_y_slices; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + for (x = 0; x < n_x_slices; ++x) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + +#if COGL_DEBUG + printf ("CREATE SLICE (%d,%d)\n", x,y); + printf ("size: (%d x %d)\n", + x_span->size - x_span->waste, + y_span->size - y_span->waste); +#endif + /* Setup texture parameters */ + GE( glBindTexture (tex->gl_target, gl_handles[y * n_x_slices + x]) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); + + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, + tex->wrap_mode) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, + tex->wrap_mode) ); + + /* Use a transparent border color so that we can leave the + color buffer alone when using texture co-ordinates + outside of the texture */ + GE( glTexParameterfv (tex->gl_target, GL_TEXTURE_BORDER_COLOR, + transparent_color) ); + + /* Pass NULL data to init size and internal format */ + GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat, + x_span->size, y_span->size, 0, + tex->gl_format, tex->gl_type, 0) ); + } + } + + return TRUE; +} + +static void +_cogl_texture_slices_free (CoglTexture *tex) +{ + if (tex->slice_x_spans != NULL) + g_array_free (tex->slice_x_spans, TRUE); + + if (tex->slice_y_spans != NULL) + g_array_free (tex->slice_y_spans, TRUE); + + if (tex->slice_gl_handles != NULL) + { + if (tex->is_foreign == FALSE) + { + GE( glDeleteTextures (tex->slice_gl_handles->len, + (GLuint*) tex->slice_gl_handles->data) ); + } + + g_array_free (tex->slice_gl_handles, TRUE); + } +} + +static gboolean +_cogl_pixel_format_from_gl_internal (GLenum gl_int_format, + CoglPixelFormat *out_format) +{ + /* It doesn't really matter we convert to exact same + format (some have no cogl match anyway) since format + is re-matched against cogl when getting or setting + texture image data. + */ + + switch (gl_int_format) + { + case GL_ALPHA: case GL_ALPHA4: case GL_ALPHA8: + case GL_ALPHA12: case GL_ALPHA16: + + *out_format = COGL_PIXEL_FORMAT_A_8; + return TRUE; + + case GL_LUMINANCE: case GL_LUMINANCE4: case GL_LUMINANCE8: + case GL_LUMINANCE12: case GL_LUMINANCE16: + + *out_format = COGL_PIXEL_FORMAT_G_8; + return TRUE; + + case GL_RGB: case GL_RGB4: case GL_RGB5: case GL_RGB8: + case GL_RGB10: case GL_RGB12: case GL_RGB16: case GL_R3_G3_B2: + + *out_format = COGL_PIXEL_FORMAT_RGB_888; + return TRUE; + + case GL_RGBA: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: + case GL_RGBA8: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16: + + *out_format = COGL_PIXEL_FORMAT_RGBA_8888; + return TRUE; + } + + return FALSE; +} + +static CoglPixelFormat +_cogl_pixel_format_to_gl (CoglPixelFormat format, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype) +{ + CoglPixelFormat required_format; + GLenum glintformat = 0; + GLenum glformat = 0; + GLenum gltype = 0; + + /* No premultiplied formats accepted by GL + * (FIXME: latest hardware?) */ + + if (format & COGL_PREMULT_BIT) + format = (format & COGL_UNPREMULT_MASK); + + /* Everything else accepted + * (FIXME: check YUV support) */ + required_format = format; + + /* Find GL equivalents */ + switch (format) + { + case COGL_PIXEL_FORMAT_A_8: + glintformat = GL_ALPHA; + glformat = GL_ALPHA; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_G_8: + glintformat = GL_LUMINANCE; + glformat = GL_LUMINANCE; + gltype = GL_UNSIGNED_BYTE; + break; + + case COGL_PIXEL_FORMAT_RGB_888: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_BGR_888: + glintformat = GL_RGB; + glformat = GL_BGR; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_RGBA_8888: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_BGRA_8888: + glintformat = GL_RGBA; + glformat = GL_BGRA; + gltype = GL_UNSIGNED_BYTE; + break; + + /* The following two types of channel ordering + * have no GL equivalent unless defined using + * system word byte ordering */ + case COGL_PIXEL_FORMAT_ARGB_8888: + glintformat = GL_RGBA; + glformat = GL_BGRA; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + gltype = GL_UNSIGNED_INT_8_8_8_8; +#else + gltype = GL_UNSIGNED_INT_8_8_8_8_REV; +#endif + break; + + case COGL_PIXEL_FORMAT_ABGR_8888: + glintformat = GL_RGBA; + glformat = GL_RGBA; +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + gltype = GL_UNSIGNED_INT_8_8_8_8; +#else + gltype = GL_UNSIGNED_INT_8_8_8_8_REV; +#endif + break; + + /* The following three types of channel ordering + * are always defined using system word byte + * ordering (even according to GLES spec) */ + case COGL_PIXEL_FORMAT_RGB_565: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_SHORT_5_6_5; + break; + case COGL_PIXEL_FORMAT_RGBA_4444: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case COGL_PIXEL_FORMAT_RGBA_5551: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_5_5_5_1; + break; + + /* FIXME: check extensions for YUV support */ + default: + break; + } + + if (out_glintformat != NULL) + *out_glintformat = glintformat; + if (out_glformat != NULL) + *out_glformat = glformat; + if (out_gltype != NULL) + *out_gltype = gltype; + + return required_format; +} + +static gboolean +_cogl_texture_bitmap_prepare (CoglTexture *tex, + CoglPixelFormat internal_format) +{ + CoglBitmap new_bitmap; + CoglPixelFormat new_data_format; + gboolean success; + + /* Was there any internal conversion requested? */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = tex->bitmap.format; + + /* Find closest format accepted by GL */ + new_data_format = _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + /* Convert to internal format */ + if (new_data_format != tex->bitmap.format) + { + success = _cogl_bitmap_convert_and_premult (&tex->bitmap, + &new_bitmap, + new_data_format); + + if (!success) + return FALSE; + + /* Update texture with new data */ + _cogl_texture_bitmap_swap (tex, &new_bitmap); + } + + return TRUE; +} + +static void +_cogl_texture_free (CoglTexture *tex) +{ + /* Frees texture resources but its handle is not + released! Do that separately before this! */ + _cogl_texture_bitmap_free (tex); + _cogl_texture_slices_free (tex); + g_free (tex); +} + +CoglHandle +cogl_texture_new_with_size (guint width, + guint height, + gint max_waste, + CoglPixelFormat internal_format) +{ + CoglTexture *tex; + gint bpp; + gint rowstride; + + /* Since no data, we need some internal format */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + /* Rowstride from width */ + bpp = _cogl_get_format_bpp (internal_format); + rowstride = width * bpp; + + /* Init texture with empty bitmap */ + tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + tex->is_foreign = FALSE; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.format = internal_format; + tex->bitmap.rowstride = rowstride; + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* Find closest GL format match */ + tex->bitmap.format = + _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + /* Create slices for the given format and size */ + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_data (guint width, + guint height, + gint max_waste, + CoglPixelFormat format, + CoglPixelFormat internal_format, + guint rowstride, + const guchar *data) +{ + CoglTexture *tex; + gint bpp; + + if (format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + if (data == NULL) + return COGL_INVALID_HANDLE; + + /* Rowstride from width if not given */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = width * bpp; + + /* Create new texture and fill with given data */ + tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + tex->is_foreign = FALSE; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.data = (guchar*)data; + tex->bitmap.format = format; + tex->bitmap.rowstride = rowstride; + tex->bitmap_owner = FALSE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle (this + * is to keep the behavior equal to _new_from_file; + * see below) */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_to_gl (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_file (const gchar *filename, + gint max_waste, + CoglPixelFormat internal_format, + GError **error) +{ + CoglBitmap bmp; + CoglTexture *tex; + + g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); + + /* Try loading with imaging backend */ + if (!_cogl_bitmap_from_file (&bmp, filename, error)) + { + /* Try fallback */ + if (!_cogl_bitmap_fallback_from_file (&bmp, filename)) + return COGL_INVALID_HANDLE; + else if (error && *error) + { + g_error_free (*error); + *error = NULL; + } + } + + /* Create new texture and fill with loaded data */ + tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + tex->is_foreign = FALSE; + + tex->bitmap = bmp; + tex->bitmap_owner = TRUE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle if the + * user decides to destroy another texture and upload + * this one instead (reloading from file is not needed + * in that case). As a rule then, everytime a valid + * CoglHandle is returned, it should also be destroyed + * with cogl_texture_unref at some point! */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_to_gl (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format) +{ + /* NOTE: width, height and internal format are not queriable + in GLES, hence such a function prototype. However, here + they are still queried from the texture for improved + robustness and for completeness in case GLES 1.0 gets + unsupported in favor of a new version and cleaner api + */ + + GLenum gl_error = 0; + GLboolean gl_istexture; + GLint gl_compressed = GL_FALSE; + GLint gl_int_format = 0; + GLint gl_width = 0; + GLint gl_height = 0; + GLint gl_min_filter; + GLint gl_mag_filter; + guint bpp; + CoglTexture *tex; + CoglTexSliceSpan x_span; + CoglTexSliceSpan y_span; + + /* Allow 2-dimensional textures only */ + if (gl_target != GL_TEXTURE_2D && + gl_target != GL_TEXTURE_RECTANGLE_ARB) + return COGL_INVALID_HANDLE; + + /* Make sure it is a valid GL texture object */ + gl_istexture = GE( glIsTexture (gl_handle) ); + if (gl_istexture == GL_FALSE) + return COGL_INVALID_HANDLE; + + /* Make sure binding succeeds */ + gl_error = glGetError (); + glBindTexture (gl_target, gl_handle); + if (glGetError () != GL_NO_ERROR) + return COGL_INVALID_HANDLE; + + /* Obtain texture parameters + (only level 0 we are interested in) */ + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &gl_int_format) ); + + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_WIDTH, + &gl_width) ); + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_HEIGHT, + &gl_height) ); + + GE( glGetTexParameteriv (gl_target, + GL_TEXTURE_MIN_FILTER, + &gl_min_filter)); + + GE( glGetTexParameteriv (gl_target, + GL_TEXTURE_MAG_FILTER, + &gl_mag_filter)); + + /* Validate width and height */ + if (gl_width <= 0 || gl_height <= 0) + return COGL_INVALID_HANDLE; + + /* Validate pot waste */ + if (x_pot_waste < 0 || x_pot_waste >= gl_width || + y_pot_waste < 0 || y_pot_waste >= gl_height) + return COGL_INVALID_HANDLE; + + /* Compressed texture images not supported */ + if (gl_compressed == GL_TRUE) + return COGL_INVALID_HANDLE; + + /* Try and match to a cogl format */ + if (!_cogl_pixel_format_from_gl_internal (gl_int_format, + &format)) + { + return COGL_INVALID_HANDLE; + } + + bpp = _cogl_get_format_bpp (format); + + /* Create new texture */ + tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + /* Setup bitmap info */ + tex->is_foreign = TRUE; + + tex->bitmap.format = format; + tex->bitmap.width = gl_width - x_pot_waste; + tex->bitmap.height = gl_height - y_pot_waste; + tex->bitmap.rowstride = tex->bitmap.width * bpp; + tex->bitmap_owner = FALSE; + + tex->gl_target = gl_target; + tex->gl_intformat = gl_int_format; + tex->gl_format = gl_int_format; + tex->gl_type = GL_UNSIGNED_BYTE; + + tex->min_filter = gl_min_filter; + tex->mag_filter = gl_mag_filter; + tex->max_waste = 0; + + /* Create slice arrays */ + tex->slice_x_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex->slice_y_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex->slice_gl_handles = + g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), 1); + + /* Store info for a single slice */ + x_span.start = 0; + x_span.size = gl_width; + x_span.waste = x_pot_waste; + g_array_append_val (tex->slice_x_spans, x_span); + + y_span.start = 0; + y_span.size = gl_height; + y_span.waste = y_pot_waste; + g_array_append_val (tex->slice_y_spans, y_span); + + g_array_append_val (tex->slice_gl_handles, gl_handle); + + /* Replace mipmap min filter modes with single level ones */ + if (gl_min_filter != GL_NEAREST && gl_min_filter != GL_LINEAR) + { + if (gl_min_filter == GL_NEAREST_MIPMAP_NEAREST) + { + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST) ); + tex->min_filter = CGL_NEAREST; + } + else + { + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) ); + tex->min_filter = CGL_LINEAR; + } + } + + /* Force appropriate wrap parameter */ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) && + gl_target == GL_TEXTURE_2D) + { + /* Hardware repeated tiling */ + tex->wrap_mode = GL_REPEAT; + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_REPEAT) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_REPEAT) ); + } + else + { + /* Any tiling will be done in software */ + tex->wrap_mode = GL_CLAMP_TO_EDGE; + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); + } + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_ref (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return COGL_INVALID_HANDLE; + + tex = _cogl_texture_pointer_from_handle (handle); + + tex->ref_count++; +#if COGL_DEBUG + printf ("COGL TEX ref %p %i\n", tex, tex->ref_count); +#endif + + return handle; +} + +void +cogl_texture_unref (CoglHandle handle) +{ + /* Check if valid texture handle */ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + +#if COGL_DEBUG + printf ("COGL TEX unref %p %i\n", tex, tex->ref_count - 1); +#endif + + if (--tex->ref_count < 1) + { +#if COGL_DEBUG + printf ("COGL TEX free %p %i\n", tex, tex->ref_count - 1); +#endif + + /* Free texture handle and resources */ + _cogl_texture_handle_release (tex); + _cogl_texture_free (tex); + } +} + +guint +cogl_texture_get_width (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.width; +} + +guint +cogl_texture_get_height (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.height; +} + +CoglPixelFormat +cogl_texture_get_format (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return COGL_PIXEL_FORMAT_ANY; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.format; +} + +guint +cogl_texture_get_rowstride (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.rowstride; +} + +gint +cogl_texture_get_max_waste (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->max_waste; +} + +gboolean +cogl_texture_is_sliced (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (tex->slice_gl_handles == NULL) + return FALSE; + + if (tex->slice_gl_handles->len <= 1) + return FALSE; + + return TRUE; +} + +gboolean +cogl_texture_get_gl_texture (CoglHandle handle, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (tex->slice_gl_handles == NULL) + return FALSE; + + if (tex->slice_gl_handles->len < 1) + return FALSE; + + if (out_gl_handle != NULL) + *out_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + + if (out_gl_target != NULL) + *out_gl_target = tex->gl_target; + + return TRUE; +} + +COGLenum +cogl_texture_get_min_filter (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->min_filter; +} + +COGLenum +cogl_texture_get_mag_filter (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->mag_filter; +} + +void +cogl_texture_set_filters (CoglHandle handle, + COGLenum min_filter, + COGLenum mag_filter) +{ + CoglTexture *tex; + GLuint gl_handle; + int i; + + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Store new values */ + tex->min_filter = min_filter; + tex->mag_filter = mag_filter; + + /* Make sure slices were created */ + if (tex->slice_gl_handles == NULL) + return; + + /* Apply new filters to every slice */ + for (i=0; islice_gl_handles->len; ++i) + { + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i); + GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); + } +} + +gboolean +cogl_texture_set_region (CoglHandle handle, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + guint dst_width, + guint dst_height, + gint width, + gint height, + CoglPixelFormat format, + guint rowstride, + const guchar *data) +{ + CoglTexture *tex; + gint bpp; + CoglBitmap source_bmp; + CoglBitmap temp_bmp; + gboolean source_bmp_owner = FALSE; + CoglPixelFormat closest_format; + GLenum closest_gl_format; + GLenum closest_gl_type; + gboolean success; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Check for valid format */ + if (format == COGL_PIXEL_FORMAT_ANY) + return FALSE; + + /* Init source bitmap */ + source_bmp.width = width; + source_bmp.height = height; + source_bmp.format = format; + source_bmp.data = (guchar*)data; + + /* Rowstride from width if none specified */ + bpp = _cogl_get_format_bpp (format); + source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; + + /* Find closest format to internal that's supported by GL */ + closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format, + NULL, /* don't need */ + &closest_gl_format, + &closest_gl_type); + + /* If no direct match, convert */ + if (closest_format != format) + { + /* Convert to required format */ + success = _cogl_bitmap_convert_and_premult (&source_bmp, + &temp_bmp, + closest_format); + + /* Swap bitmaps if succeeded */ + if (!success) return FALSE; + source_bmp = temp_bmp; + source_bmp_owner = TRUE; + } + + /* Send data to GL */ + _cogl_texture_upload_subregion_to_gl (tex, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + &source_bmp, + closest_gl_format, + closest_gl_type); + + /* Free data if owner */ + if (source_bmp_owner) + g_free (source_bmp.data); + + return TRUE; +} + +gint +cogl_texture_get_data (CoglHandle handle, + CoglPixelFormat format, + guint rowstride, + guchar *data) +{ + CoglTexture *tex; + gint bpp; + gint byte_size; + CoglPixelFormat closest_format; + gint closest_bpp; + GLenum closest_gl_format; + GLenum closest_gl_type; + CoglBitmap target_bmp; + CoglBitmap new_bmp; + gboolean success; + guchar *src; + guchar *dst; + gint y; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = tex->bitmap.format; + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = tex->bitmap.width * bpp; + + /* Return byte size if only that requested */ + byte_size = tex->bitmap.height * rowstride; + if (data == NULL) return byte_size; + + /* Find closest format that's supported by GL */ + closest_format = _cogl_pixel_format_to_gl (format, + NULL, /* don't need */ + &closest_gl_format, + &closest_gl_type); + + closest_bpp = _cogl_get_format_bpp (closest_format); + + /* Is the requested format supported? */ + if (closest_format == format) + { + /* Target user data directly */ + target_bmp = tex->bitmap; + target_bmp.format = format; + target_bmp.rowstride = rowstride; + target_bmp.data = data; + } + else + { + /* Target intermediate buffer */ + target_bmp = tex->bitmap; + target_bmp.format = closest_format; + target_bmp.rowstride = target_bmp.width * closest_bpp; + target_bmp.data = (guchar*) g_malloc (target_bmp.height + * target_bmp.rowstride); + } + + /* Retrieve data from slices */ + _cogl_texture_download_from_gl (tex, &target_bmp, + closest_gl_format, + closest_gl_type); + + /* Was intermediate used? */ + if (closest_format != format) + { + /* Convert to requested format */ + success = _cogl_bitmap_convert_and_premult (&target_bmp, + &new_bmp, + format); + + /* Free intermediate data and return if failed */ + g_free (target_bmp.data); + if (!success) return 0; + + /* Copy to user buffer */ + for (y = 0; y < new_bmp.height; ++y) + { + src = new_bmp.data + y * new_bmp.rowstride; + dst = data + y * rowstride; + memcpy (dst, src, new_bmp.width); + } + + /* Free converted data */ + g_free (new_bmp.data); + } + + return byte_size; +} + +static void +_cogl_texture_quad_sw (CoglTexture *tex, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglSpanIter iter_x , iter_y; + ClutterFixed tw , th; + ClutterFixed tqx , tqy; + ClutterFixed first_tx , first_ty; + ClutterFixed first_qx , first_qy; + ClutterFixed slice_tx1 , slice_ty1; + ClutterFixed slice_tx2 , slice_ty2; + ClutterFixed slice_qx1 , slice_qy1; + ClutterFixed slice_qx2 , slice_qy2; + GLuint gl_handle; + +#if COGL_DEBUG + printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); +#endif + + /* Prepare GL state */ + gulong enable_flags = 0; + + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + { + enable_flags |= COGL_ENABLE_BLEND; + } + + cogl_enable (enable_flags); + + /* Scale ratio from texture to quad widths */ + tw = CLUTTER_INT_TO_FIXED (tex->bitmap.width); + th = CLUTTER_INT_TO_FIXED (tex->bitmap.height); + + tqx = CFX_QDIV (x2-x1, CFX_QMUL (tw, (tx2 - tx1))); + tqy = CFX_QDIV (y2-y1, CFX_QMUL (th, (ty2 - ty1))); + + /* Integral texture coordinate for first tile */ + first_tx = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (tx1)); + first_ty = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (ty1)); + + /* Denormalize texture coordinates */ + first_tx = CFX_QMUL (first_tx, tw); + first_ty = CFX_QMUL (first_ty, th); + tx1 = CFX_QMUL (tx1, tw); + ty1 = CFX_QMUL (ty1, th); + tx2 = CFX_QMUL (tx2, tw); + ty2 = CFX_QMUL (ty2, th); + + /* Quad coordinate of the first tile */ + first_qx = x1 - CFX_QMUL (tx1 - first_tx, tqx); + first_qy = y1 - CFX_QMUL (ty1 - first_ty, tqy); + + + /* Iterate until whole quad height covered */ + for (_cogl_span_iter_begin (&iter_y, tex->slice_y_spans, + first_ty, ty1, ty2) ; + !_cogl_span_iter_end (&iter_y) ; + _cogl_span_iter_next (&iter_y) ) + { + /* Discard slices out of quad early */ + if (!iter_y.intersects) continue; + + /* Span-quad intersection in quad coordinates */ + slice_qy1 = first_qy + + CFX_QMUL (iter_y.intersect_start - first_ty, tqy); + + slice_qy2 = first_qy + + CFX_QMUL (iter_y.intersect_end - first_ty, tqy); + + /* Localize slice texture coordinates */ + slice_ty1 = iter_y.intersect_start - iter_y.pos; + slice_ty2 = iter_y.intersect_end - iter_y.pos; + + /* Normalize texture coordinates to current slice + (rectangle texture targets take denormalized) */ + if (tex->gl_target != GL_TEXTURE_RECTANGLE_ARB) + { + slice_ty1 /= iter_y.span->size; + slice_ty2 /= iter_y.span->size; + } + + + /* Iterate until whole quad width covered */ + for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, + first_tx, tx1, tx2) ; + !_cogl_span_iter_end (&iter_x) ; + _cogl_span_iter_next (&iter_x) ) + { + /* Discard slices out of quad early */ + if (!iter_x.intersects) continue; + + /* Span-quad intersection in quad coordinates */ + slice_qx1 = first_qx + + CFX_QMUL (iter_x.intersect_start - first_tx, tqx); + + slice_qx2 = first_qx + + CFX_QMUL (iter_x.intersect_end - first_tx, tqx); + + /* Localize slice texture coordinates */ + slice_tx1 = iter_x.intersect_start - iter_x.pos; + slice_tx2 = iter_x.intersect_end - iter_x.pos; + + /* Normalize texture coordinates to current slice + (rectangle texture targets take denormalized) */ + if (tex->gl_target != GL_TEXTURE_RECTANGLE_ARB) + { + slice_tx1 /= iter_x.span->size; + slice_tx2 /= iter_x.span->size; + } + +#if COGL_DEBUG + printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); + printf("qx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx1)); + printf("qy1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy1)); + printf("qx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx2)); + printf("qy2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy2)); + printf("tx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx1)); + printf("ty1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty1)); + printf("tx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx2)); + printf("ty2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty2)); +#endif + + /* Pick and bind opengl texture object */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + iter_y.index * iter_x.array->len + + iter_x.index); + + GE( glBindTexture (tex->gl_target, gl_handle) ); + +#define CFX_F CLUTTER_FIXED_TO_FLOAT + + /* Draw textured quad */ + GE( glBegin (GL_QUADS) ); + + GE( glTexCoord2f (CFX_F(slice_tx1), CFX_F(slice_ty1)) ); + GE( glVertex2f (CFX_F(slice_qx1), CFX_F(slice_qy1)) ); + + GE( glTexCoord2f (CFX_F(slice_tx2), CFX_F(slice_ty1)) ); + GE( glVertex2f (CFX_F(slice_qx2), CFX_F(slice_qy1)) ); + + GE( glTexCoord2f (CFX_F(slice_tx2), CFX_F(slice_ty2)) ); + GE( glVertex2f (CFX_F(slice_qx2), CFX_F(slice_qy2)) ); + + GE( glTexCoord2f (CFX_F(slice_tx1), CFX_F(slice_ty2)) ); + GE( glVertex2f (CFX_F(slice_qx1), CFX_F(slice_qy2)) ); + + glEnd (); + +#undef CFX_F + } + } +} + +static void +_cogl_texture_quad_hw (CoglTexture *tex, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + +#if COGL_DEBUG + printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); +#endif + + /* Prepare GL state */ + gulong enable_flags = 0; + + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + enable_flags |= COGL_ENABLE_TEXTURE_RECT; + else + enable_flags |= COGL_ENABLE_TEXTURE_2D; + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + { + enable_flags |= COGL_ENABLE_BLEND; + } + + cogl_enable (enable_flags); + + /* Pick and bind opengl texture object */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + GE( glBindTexture (tex->gl_target, gl_handle) ); + + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + + /* Don't include the waste in the texture coordinates */ + tx1 = tx1 * (x_span->size - x_span->waste) / x_span->size; + tx2 = tx2 * (x_span->size - x_span->waste) / x_span->size; + ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; + ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; + + /* Denormalize texture coordinates for rectangle textures */ + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + tx1 *= x_span->size; + tx2 *= x_span->size; + ty1 *= y_span->size; + ty2 *= y_span->size; + } + +#define CFX_F(x) CLUTTER_FIXED_TO_FLOAT(x) + + /* Draw textured quad */ + GE( glBegin (GL_QUADS) ); + + GE( glTexCoord2f (CFX_F(tx1), CFX_F(ty1)) ); + GE( glVertex2f (CFX_F(x1), CFX_F(y1)) ); + + GE( glTexCoord2f (CFX_F(tx2), CFX_F(ty1)) ); + GE( glVertex2f (CFX_F(x2), CFX_F(y1)) ); + + GE( glTexCoord2f (CFX_F(tx2), CFX_F(ty2)) ); + GE( glVertex2f (CFX_F(x2), CFX_F(y2)) ); + + GE( glTexCoord2f (CFX_F(tx1), CFX_F(ty2)) ); + GE( glVertex2f (CFX_F(x1), CFX_F(y2)) ); + + glEnd (); + +#undef CFX_F +} + +void +cogl_texture_rectangle (CoglHandle handle, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + CoglTexture *tex; + ClutterFixed tempx; + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Make sure we got stuff to draw */ + if (tex->slice_gl_handles == NULL) + return; + + if (tex->slice_gl_handles->len == 0) + return; + + if (tx1 == tx2 || ty1 == ty2) + return; + + /* Fix quad coord ordering + (atm this is required for sw tiling to iterate + over slices properly) */ + if (x1 > x2) + { + tempx = x1; + x1 = x2; + x2 = tempx; + } + + if (y1 > y2) + { + tempx = y1; + y1 = y2; + y2 = tempx; + } + + /* Fix texture coord ordering */ + if (tx1 > tx2) + { + tempx = tx1; + tx1 = tx2; + tx2 = tempx; + } + + if (ty1 > ty2) + { + tempx = ty1; + ty1 = ty2; + ty2 = tempx; + } + + /* Pick tiling mode according to hw support */ + if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) + && tex->slice_gl_handles->len == 1) + { + _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } + else + { + if (tex->slice_gl_handles->len == 1 + && tx1 >= -CFX_ONE && tx2 <= CFX_ONE + && ty1 >= -CFX_ONE && ty2 <= CFX_ONE) + { + _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } + else + { + _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } + } +} + +void +cogl_texture_polygon (CoglHandle handle, + guint n_vertices, + CoglTextureVertex *vertices, + gboolean use_color) +{ + CoglTexture *tex; + int i, x, y, vnum; + GLuint gl_handle; + CoglTexSliceSpan *y_span, *x_span; + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* The polygon will have artifacts where the slices join if the wrap + mode is GL_LINEAR because the filtering will pull in pixels from + the transparent border. To make it clear that the function + shouldn't be used in these circumstances we just bail out and + draw nothing */ + if (tex->slice_gl_handles->len != 1 + && (tex->min_filter != GL_NEAREST || tex->mag_filter != GL_NEAREST)) + { + static gboolean shown_warning = FALSE; + + if (!shown_warning) + { + g_warning ("cogl_texture_polygon does not work for sliced textures " + "when the minification and magnification filters are not " + "CGL_NEAREST"); + shown_warning = TRUE; + } + return; + } + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Prepare GL state */ + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + cogl_enable (COGL_ENABLE_TEXTURE_RECT | COGL_ENABLE_BLEND); + else + cogl_enable (COGL_ENABLE_TEXTURE_2D | COGL_ENABLE_BLEND); + + /* Temporarily change the wrapping mode on all of the slices to use + a transparent border */ + for (i = 0; i < tex->slice_gl_handles->len; i++) + { + GE( glBindTexture (tex->gl_target, + g_array_index (tex->slice_gl_handles, GLuint, i)) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_BORDER) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_BORDER) ); + } + + i = 0; + + /* Render all of the slices with the full geometry but use a + transparent border color so that any part of the texture not + covered by the slice will be ignored */ + for (y = 0; y < tex->slice_y_spans->len; y++) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + for (x = 0; x < tex->slice_x_spans->len; x++) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i++); + + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glBegin (GL_TRIANGLE_FAN) ); + + for (vnum = 0; vnum < n_vertices; vnum++) + { + GLfloat tx, ty; + + if (use_color) + cogl_color (&vertices[vnum].color); + + /* Transform the texture co-ordinates so they are + relative to the slice */ + tx = (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].tx) + - x_span->start / (GLfloat) tex->bitmap.width) + * tex->bitmap.width / x_span->size; + ty = (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].ty) + - y_span->start / (GLfloat) tex->bitmap.height) + * tex->bitmap.height / y_span->size; + + if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + { + tx *= x_span->size; + ty *= y_span->size; + } + + GE( glTexCoord2f (tx, ty) ); + + GE( glVertex3f (CLUTTER_FIXED_TO_FLOAT (vertices[vnum].x), + CLUTTER_FIXED_TO_FLOAT (vertices[vnum].y), + CLUTTER_FIXED_TO_FLOAT (vertices[vnum].z)) ); + } + + GE( glEnd () ); + } + } + + /* Restore the wrapping mode */ + for (i = 0; i < tex->slice_gl_handles->len; i++) + { + GE( glBindTexture (tex->gl_target, + g_array_index (tex->slice_gl_handles, GLuint, i)) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, tex->wrap_mode) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, tex->wrap_mode) ); + } +} diff --git a/clutter/cogl/gl/cogl-texture.h b/clutter/cogl/gl/cogl-texture.h new file mode 100644 index 000000000..d4104804b --- /dev/null +++ b/clutter/cogl/gl/cogl-texture.h @@ -0,0 +1,64 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_TEXTURE_H +#define __COGL_TEXTURE_H + +#include "cogl-bitmap.h" + +typedef struct _CoglTexture CoglTexture; +typedef struct _CoglTexSliceSpan CoglTexSliceSpan; +typedef struct _CoglSpanIter CoglSpanIter; + +struct _CoglTexSliceSpan +{ + gint start; + gint size; + gint waste; +}; + +struct _CoglTexture +{ + guint ref_count; + CoglBitmap bitmap; + gboolean bitmap_owner; + GLenum gl_target; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + GArray *slice_x_spans; + GArray *slice_y_spans; + GArray *slice_gl_handles; + gint max_waste; + COGLenum min_filter; + COGLenum mag_filter; + gboolean is_foreign; + GLint wrap_mode; +}; + +CoglTexture* +_cogl_texture_pointer_from_handle (CoglHandle handle); + +#endif /* __COGL_TEXTURE_H */ diff --git a/clutter/cogl/gl/cogl.c b/clutter/cogl/gl/cogl.c index 5e752cd84..3e8fc350b 100644 --- a/clutter/cogl/gl/cogl.c +++ b/clutter/cogl/gl/cogl.c @@ -39,23 +39,12 @@ typedef CoglFuncPtr (*GLXGetProcAddressProc) (const guint8 *procName); #endif -static gulong __enable_flags = 0; +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" -/* FBO Procs */ -typedef void (*GenFramebuffers) (GLsizei n, GLuint *ids); -typedef void (*BindFramebuffer) (GLenum target, GLuint framebuffer); -typedef void (*FramebufferTexture2D) (GLenum target, GLenum attachment, - GLenum textarget, GLuint texture, - GLint level); -typedef GLenum (*CheckFramebufferStatus)(GLenum target); -typedef void (*DeleteFramebuffers) (GLsizei n, const GLuint *framebuffers); - -static GenFramebuffers _gen_framebuffers = NULL; -static BindFramebuffer _bind_framebuffer = NULL; -static FramebufferTexture2D _framebuffer_texture_2d = NULL; -static CheckFramebufferStatus _check_framebuffer_status = NULL; -static DeleteFramebuffers _delete_framebuffers = NULL; +/* GL error to string conversion */ #if COGL_DEBUG struct token_string { @@ -89,19 +78,21 @@ error_string(GLenum errorCode) } #endif -#if COGL_DEBUG -#define GE(x...) G_STMT_START { \ - GLenum err; \ - (x); \ - while ((err = glGetError()) != GL_NO_ERROR) { \ - fprintf(stderr, "glError: %s caught at %s:%u\n", \ - (char *)error_string(err), \ - __FILE__, __LINE__); \ - } \ -} G_STMT_END -#else -#define GE(x) (x); -#endif + +/* Expecting ARB functions not to be defined */ +#define glCreateProgramObjectARB ctx->pf_glCreateProgramObjectARB +#define glCreateShaderObjectARB ctx->pf_glCreateShaderObjectARB +#define glShaderSourceARB ctx->pf_glShaderSourceARB +#define glCompileShaderARB ctx->pf_glCompileShaderARB +#define glAttachObjectARB ctx->pf_glAttachObjectARB +#define glLinkProgramARB ctx->pf_glLinkProgramARB +#define glUseProgramObjectARB ctx->pf_glUseProgramObjectARB +#define glGetUniformLocationARB ctx->pf_glGetUniformLocationARB +#define glDeleteObjectARB ctx->pf_glDeleteObjectARB +#define glGetInfoLogARB ctx->pf_glGetInfoLogARB +#define glGetObjectParameterivARB ctx->pf_glGetObjectParameterivARB +#define glUniform1fARB ctx->pf_glUniform1fARB + CoglFuncPtr cogl_get_proc_address (const gchar* name) @@ -217,9 +208,6 @@ cogl_paint_init (const ClutterColor *color) * glDepthFunc (GL_LEQUAL); * glAlphaFunc (GL_GREATER, 0.1); */ - - cogl_enable (CGL_ENABLE_BLEND); - glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } /* FIXME: inline most of these */ @@ -272,59 +260,155 @@ cogl_rotate (gint angle, gint x, gint y, gint z) glRotatef ((float)angle, (float)x, (float)y, (float)z); } +static inline gboolean +cogl_toggle_flag (CoglContext *ctx, + gulong new_flags, + gulong flag, + GLenum gl_flag) +{ + /* Toggles and caches a single enable flag on or off + * by comparing to current state + */ + if (new_flags & flag) + { + if (!(ctx->enable_flags & flag)) + { + GE( glEnable (gl_flag) ); + ctx->enable_flags |= flag; + return TRUE; + } + } + else if (ctx->enable_flags & flag) + { + GE( glDisable (gl_flag) ); + ctx->enable_flags &= ~flag; + } + + return FALSE; +} + +static inline gboolean +cogl_toggle_client_flag (CoglContext *ctx, + gulong new_flags, + gulong flag, + GLenum gl_flag) +{ + /* Toggles and caches a single client-side enable flag + * on or off by comparing to current state + */ + if (new_flags & flag) + { + if (!(ctx->enable_flags & flag)) + { + GE( glEnableClientState (gl_flag) ); + ctx->enable_flags |= flag; + return TRUE; + } + } + else if (ctx->enable_flags & flag) + { + GE( glDisableClientState (gl_flag) ); + ctx->enable_flags &= ~flag; + } + + return FALSE; +} + void cogl_enable (gulong flags) { /* This function essentially caches glEnable state() in the * hope of lessening number GL traffic. */ - if (flags & CGL_ENABLE_BLEND) + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (cogl_toggle_flag (ctx, flags, + COGL_ENABLE_BLEND, + GL_BLEND)) { - if (!(__enable_flags & CGL_ENABLE_BLEND)) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_2D, + GL_TEXTURE_2D); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_VERTEX_ARRAY, + GL_VERTEX_ARRAY); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_TEXCOORD_ARRAY, + GL_TEXTURE_COORD_ARRAY); + +#ifdef GL_TEXTURE_RECTANGLE_ARB + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_RECT, + GL_TEXTURE_RECTANGLE_ARB); +#endif +} + +gulong +cogl_get_enable () +{ + _COGL_GET_CONTEXT (ctx, 0); + + return ctx->enable_flags; +} + +/* +void +cogl_enable (gulong flags) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (flags & COGL_ENABLE_BLEND) + { + if (!(ctx->enable_flags & COGL_ENABLE_BLEND)) { glEnable (GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - __enable_flags |= CGL_ENABLE_BLEND; + ctx->enable_flags |= COGL_ENABLE_BLEND; } } - else if (__enable_flags & CGL_ENABLE_BLEND) + else if (ctx->enable_flags & COGL_ENABLE_BLEND) { glDisable (GL_BLEND); - __enable_flags &= ~CGL_ENABLE_BLEND; + ctx->enable_flags &= ~COGL_ENABLE_BLEND; } - if (flags & CGL_ENABLE_TEXTURE_2D) + if (flags & COGL_ENABLE_TEXTURE_2D) { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_2D)) + if (!(ctx->enable_flags & COGL_ENABLE_TEXTURE_2D)) { glEnable (GL_TEXTURE_2D); - __enable_flags |= CGL_ENABLE_TEXTURE_2D; + ctx->enable_flags |= COGL_ENABLE_TEXTURE_2D; } } - else if (__enable_flags & CGL_ENABLE_TEXTURE_2D) + else if (ctx->enable_flags & COGL_ENABLE_TEXTURE_2D) { glDisable (GL_TEXTURE_2D); - __enable_flags &= ~CGL_ENABLE_TEXTURE_2D; + ctx->enable_flags &= ~COGL_ENABLE_TEXTURE_2D; } - + #ifdef GL_TEXTURE_RECTANGLE_ARB - if (flags & CGL_ENABLE_TEXTURE_RECT) + if (flags & COGL_ENABLE_TEXTURE_RECT) { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_RECT)) + if (!(ctx->enable_flags & COGL_ENABLE_TEXTURE_RECT)) { glEnable (GL_TEXTURE_RECTANGLE_ARB); - __enable_flags |= CGL_ENABLE_TEXTURE_RECT; + ctx->enable_flags |= COGL_ENABLE_TEXTURE_RECT; } } - else if (__enable_flags & CGL_ENABLE_TEXTURE_RECT) + else if (ctx->enable_flags & COGL_ENABLE_TEXTURE_RECT) { glDisable (GL_TEXTURE_RECTANGLE_ARB); - __enable_flags &= ~CGL_ENABLE_TEXTURE_RECT; + ctx->enable_flags &= ~COGL_ENABLE_TEXTURE_RECT; } #endif } - +*/ void cogl_enable_depth_test (gboolean setting) { @@ -345,7 +429,15 @@ cogl_enable_depth_test (gboolean setting) void cogl_color (const ClutterColor *color) { - glColor4ub (color->red, color->green, color->blue, color->alpha); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + glColor4ub (color->red, + color->green, + color->blue, + color->alpha); + + /* Store alpha for proper blending enables */ + ctx->color_alpha = color->alpha; } void @@ -354,197 +446,63 @@ cogl_clip_set (ClutterFixed x_offset, ClutterFixed width, ClutterFixed height) { - GE( glEnable (GL_STENCIL_TEST) ); + if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) + { + GLdouble eqn_left[4] = { 1.0, 0, 0, + -CLUTTER_FIXED_TO_FLOAT (x_offset) }; + GLdouble eqn_right[4] = { -1.0, 0, 0, + CLUTTER_FIXED_TO_FLOAT (x_offset + width) }; + GLdouble eqn_top[4] = { 0, 1.0, 0, -CLUTTER_FIXED_TO_FLOAT (y_offset) }; + GLdouble eqn_bottom[4] = { 0, -1.0, 0, CLUTTER_FIXED_TO_FLOAT + (y_offset + height) }; - GE( glClearStencil (0.0f) ); - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + GE( glClipPlane (GL_CLIP_PLANE0, eqn_left) ); + GE( glClipPlane (GL_CLIP_PLANE1, eqn_right) ); + GE( glClipPlane (GL_CLIP_PLANE2, eqn_top) ); + GE( glClipPlane (GL_CLIP_PLANE3, eqn_bottom) ); + GE( glEnable (GL_CLIP_PLANE0) ); + GE( glEnable (GL_CLIP_PLANE1) ); + GE( glEnable (GL_CLIP_PLANE2) ); + GE( glEnable (GL_CLIP_PLANE3) ); + } + else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) + { + GE( glEnable (GL_STENCIL_TEST) ); - GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); - GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); + GE( glClearStencil (0.0f) ); + GE( glClear (GL_STENCIL_BUFFER_BIT) ); - GE( glColor3f (1.0f, 1.0f, 1.0f) ); + GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); + GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); - GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset), - CLUTTER_FIXED_TO_FLOAT (y_offset), - CLUTTER_FIXED_TO_FLOAT (x_offset + width), - CLUTTER_FIXED_TO_FLOAT (y_offset + height)) ); + GE( glColor3f (1.0f, 1.0f, 1.0f) ); - GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); - GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + GE( glRectf (CLUTTER_FIXED_TO_FLOAT (x_offset), + CLUTTER_FIXED_TO_FLOAT (y_offset), + CLUTTER_FIXED_TO_FLOAT (x_offset + width), + CLUTTER_FIXED_TO_FLOAT (y_offset + height)) ); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + } } void cogl_clip_unset (void) { - GE( glDisable (GL_STENCIL_TEST) ); -} - -gboolean -cogl_texture_can_size (COGLenum target, - COGLenum pixel_format, - COGLenum pixel_type, - int width, - int height) -{ -#ifdef GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB - if (target == CGL_TEXTURE_RECTANGLE_ARB) + if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) { - GLint max_size = 0; - - GE( glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &max_size) ); - - return (max_size && width <= max_size && height <= max_size); + GE( glDisable (GL_CLIP_PLANE3) ); + GE( glDisable (GL_CLIP_PLANE2) ); + GE( glDisable (GL_CLIP_PLANE1) ); + GE( glDisable (GL_CLIP_PLANE0) ); } - else /* Assumes CGL_TEXTURE_2D */ -#endif + else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) { - GLint new_width = 0; - - GE( glTexImage2D (GL_PROXY_TEXTURE_2D, 0, GL_RGBA, - width, height, 0 /* border */, - pixel_format, pixel_type, NULL) ); - - GE( glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, - GL_TEXTURE_WIDTH, &new_width) ); - - return new_width != 0; + GE( glDisable (GL_STENCIL_TEST) ); } } -void -cogl_texture_quad (gint x1, - gint x2, - gint y1, - gint y2, - ClutterFixed tx1, - ClutterFixed ty1, - ClutterFixed tx2, - ClutterFixed ty2) -{ - gdouble txf1, tyf1, txf2, tyf2; - - txf1 = CLUTTER_FIXED_TO_DOUBLE (tx1); - tyf1 = CLUTTER_FIXED_TO_DOUBLE (ty1); - txf2 = CLUTTER_FIXED_TO_DOUBLE (tx2); - tyf2 = CLUTTER_FIXED_TO_DOUBLE (ty2); - - glBegin (GL_QUADS); - glTexCoord2f (txf2, tyf2); glVertex2i (x2, y2); - glTexCoord2f (txf1, tyf2); glVertex2i (x1, y2); - glTexCoord2f (txf1, tyf1); glVertex2i (x1, y1); - glTexCoord2f (txf2, tyf1); glVertex2i (x2, y1); - glEnd (); -} - -void -cogl_textures_create (guint num, COGLuint *textures) -{ - GE( glGenTextures (num, textures) ); -} - -void -cogl_textures_destroy (guint num, const COGLuint *textures) -{ - GE( glDeleteTextures (num, textures) ); -} - -void -cogl_texture_bind (COGLenum target, COGLuint texture) -{ - GE( glBindTexture (target, texture) ); -} - -void -cogl_texture_set_alignment (COGLenum target, - guint alignment, - guint row_length) -{ - GE( glPixelStorei (GL_UNPACK_ROW_LENGTH, row_length) ); - GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) ); -} - -void -cogl_texture_set_filters (COGLenum target, - COGLenum min_filter, - COGLenum max_filter) -{ - GE( glTexParameteri(target, GL_TEXTURE_MAG_FILTER, max_filter) ); - GE( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filter) ); -} - -void -cogl_texture_set_wrap (COGLenum target, - COGLenum wrap_s, - COGLenum wrap_t) -{ - GE( glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap_s) ); - GE( glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap_s) ); -} - -void -cogl_texture_image_2d (COGLenum target, - COGLint internal_format, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels) -{ - GE( glTexImage2D (target, - 0, /* No mipmap support as yet */ - internal_format, - width, - height, - 0, /* 0 pixel border */ - format, - type, - pixels) ); -} - -void -cogl_texture_sub_image_2d (COGLenum target, - gint xoff, - gint yoff, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels) -{ - GE( glTexSubImage2D (target, - 0, - xoff, - yoff, - width, - height, - format, - type, - pixels)); -} - -void -cogl_rectangle (gint x, gint y, guint width, guint height) -{ - GE( glRecti (x, y, x + width, y + height) ); -} - -/* FIXME: Should use ClutterReal or Fixed */ -void -cogl_trapezoid (gint y1, - gint x11, - gint x21, - gint y2, - gint x12, - gint x22) -{ - GE( glBegin (GL_QUADS) ); - GE( glVertex2i (x11, y1) ); - GE( glVertex2i (x21, y1) ); - GE( glVertex2i (x22, y2) ); - GE( glVertex2i (x12, y2) ); - GE( glEnd () ); -} - void cogl_alpha_func (COGLenum func, ClutterFixed ref) @@ -649,13 +607,17 @@ cogl_setup_viewport (guint width, GE( glTranslatef (0.0f, -1.0 * height, 0.0f) ); } -ClutterFeatureFlags -cogl_get_features () +static void +_cogl_features_init () { + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + ClutterFeatureFlags flags = 0; const gchar *gl_extensions; + int max_clip_planes = 0; + int stencil_bits = 0; - flags = CLUTTER_FEATURE_TEXTURE_READ_PIXELS; + flags = COGL_FEATURE_TEXTURE_READ_PIXELS; gl_extensions = (const gchar*) glGetString (GL_EXTENSIONS); @@ -663,53 +625,194 @@ cogl_get_features () if (cogl_check_extension ("GL_ARB_texture_rectangle", gl_extensions) || cogl_check_extension ("GL_EXT_texture_rectangle", gl_extensions)) { - flags |= CLUTTER_FEATURE_TEXTURE_RECTANGLE; + flags |= COGL_FEATURE_TEXTURE_RECTANGLE; } #endif - + + if (cogl_check_extension ("GL_ARB_texture_non_power_of_two", gl_extensions)) + { + flags |= COGL_FEATURE_TEXTURE_NPOT; + } + #ifdef GL_YCBCR_MESA if (cogl_check_extension ("GL_MESA_ycbcr_texture", gl_extensions)) { - flags |= CLUTTER_FEATURE_TEXTURE_YUV; + flags |= COGL_FEATURE_TEXTURE_YUV; } #endif - if (cogl_check_extension ("GL_ARB_vertex_shader", gl_extensions) && + if (cogl_check_extension ("GL_ARB_shader_objects", gl_extensions) && + cogl_check_extension ("GL_ARB_vertex_shader", gl_extensions) && cogl_check_extension ("GL_ARB_fragment_shader", gl_extensions)) { - flags |= CLUTTER_FEATURE_SHADERS_GLSL; + ctx->pf_glCreateProgramObjectARB = + (PFNGLCREATEPROGRAMOBJECTARBPROC) + cogl_get_proc_address ("glCreateProgramObjectARB"); + + ctx->pf_glCreateShaderObjectARB = + (PFNGLCREATESHADEROBJECTARBPROC) + cogl_get_proc_address ("glCreateShaderObjectARB"); + + ctx->pf_glShaderSourceARB = + (PFNGLSHADERSOURCEARBPROC) + cogl_get_proc_address ("glShaderSourceARB"); + + ctx->pf_glCompileShaderARB = + (PFNGLCOMPILESHADERARBPROC) + cogl_get_proc_address ("glCompileShaderARB"); + + ctx->pf_glAttachObjectARB = + (PFNGLATTACHOBJECTARBPROC) + cogl_get_proc_address ("glAttachObjectARB"); + + ctx->pf_glLinkProgramARB = + (PFNGLLINKPROGRAMARBPROC) + cogl_get_proc_address ("glLinkProgramARB"); + + ctx->pf_glUseProgramObjectARB = + (PFNGLUSEPROGRAMOBJECTARBPROC) + cogl_get_proc_address ("glUseProgramObjectARB"); + + ctx->pf_glGetUniformLocationARB = + (PFNGLGETUNIFORMLOCATIONARBPROC) + cogl_get_proc_address ("glGetUniformLocationARB"); + + ctx->pf_glDeleteObjectARB = + (PFNGLDELETEOBJECTARBPROC) + cogl_get_proc_address ("glDeleteObjectARB"); + + ctx->pf_glGetInfoLogARB = + (PFNGLGETINFOLOGARBPROC) + cogl_get_proc_address ("glGetInfoLogARB"); + + ctx->pf_glGetObjectParameterivARB = + (PFNGLGETOBJECTPARAMETERIVARBPROC) + cogl_get_proc_address ("glGetObjectParameterivARB"); + + ctx->pf_glUniform1fARB = + (PFNGLUNIFORM1FARBPROC) + cogl_get_proc_address ("glUniform1fARB"); + + if (ctx->pf_glCreateProgramObjectARB && + ctx->pf_glCreateShaderObjectARB && + ctx->pf_glShaderSourceARB && + ctx->pf_glCompileShaderARB && + ctx->pf_glAttachObjectARB && + ctx->pf_glLinkProgramARB && + ctx->pf_glUseProgramObjectARB && + ctx->pf_glGetUniformLocationARB && + ctx->pf_glDeleteObjectARB && + ctx->pf_glGetInfoLogARB && + ctx->pf_glGetObjectParameterivARB && + ctx->pf_glUniform1fARB) + flags |= COGL_FEATURE_SHADERS_GLSL; } + if (cogl_check_extension ("GL_EXT_framebuffer_object", gl_extensions) || cogl_check_extension ("GL_ARB_framebuffer_object", gl_extensions)) + { + ctx->pf_glGenRenderbuffersEXT = + (PFNGLGENRENDERBUFFERSEXTPROC) + cogl_get_proc_address ("glGenRenderbuffersEXT"); + + ctx->pf_glBindRenderbufferEXT = + (PFNGLBINDRENDERBUFFEREXTPROC) + cogl_get_proc_address ("glBindRenderbufferEXT"); + + ctx->pf_glRenderbufferStorageEXT = + (PFNGLRENDERBUFFERSTORAGEEXTPROC) + cogl_get_proc_address ("glRenderbufferStorageEXT"); + + ctx->pf_glGenFramebuffersEXT = + (PFNGLGENFRAMEBUFFERSEXTPROC) + cogl_get_proc_address ("glGenFramebuffersEXT"); + + ctx->pf_glBindFramebufferEXT = + (PFNGLBINDFRAMEBUFFEREXTPROC) + cogl_get_proc_address ("glBindFramebufferEXT"); + + ctx->pf_glFramebufferTexture2DEXT = + (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) + cogl_get_proc_address ("glFramebufferTexture2DEXT"); + + ctx->pf_glFramebufferRenderbufferEXT = + (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) + cogl_get_proc_address ("glFramebufferRenderbufferEXT"); + + ctx->pf_glCheckFramebufferStatusEXT = + (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) + cogl_get_proc_address ("glCheckFramebufferStatusEXT"); + + ctx->pf_glDeleteFramebuffersEXT = + (PFNGLDELETEFRAMEBUFFERSEXTPROC) + cogl_get_proc_address ("glDeleteFramebuffersEXT"); + + if (ctx->pf_glGenRenderbuffersEXT && + ctx->pf_glBindRenderbufferEXT && + ctx->pf_glRenderbufferStorageEXT && + ctx->pf_glGenFramebuffersEXT && + ctx->pf_glBindFramebufferEXT && + ctx->pf_glFramebufferTexture2DEXT && + ctx->pf_glFramebufferRenderbufferEXT && + ctx->pf_glCheckFramebufferStatusEXT && + ctx->pf_glDeleteFramebuffersEXT) + flags |= COGL_FEATURE_OFFSCREEN; + } + + if (cogl_check_extension ("GL_EXT_framebuffer_blit", gl_extensions)) { - _gen_framebuffers = - (GenFramebuffers) cogl_get_proc_address ("glGenFramebuffersEXT"); - - _bind_framebuffer = - (BindFramebuffer) cogl_get_proc_address ("glBindFramebufferEXT"); - - _framebuffer_texture_2d = - (FramebufferTexture2D) - cogl_get_proc_address ("glFramebufferTexture2DEXT"); - - _check_framebuffer_status = - (CheckFramebufferStatus) - cogl_get_proc_address ("glCheckFramebufferStatusEXT"); - - _delete_framebuffers = - (DeleteFramebuffers) - cogl_get_proc_address ("glDeleteFramebuffersEXT"); - - if (_gen_framebuffers - && _bind_framebuffer - && _framebuffer_texture_2d - && _check_framebuffer_status - && _delete_framebuffers) - flags |= CLUTTER_FEATURE_OFFSCREEN; + ctx->pf_glBlitFramebufferEXT = + (PFNGLBLITFRAMEBUFFEREXTPROC) + cogl_get_proc_address ("glBlitFramebufferEXT"); + + if (ctx->pf_glBlitFramebufferEXT) + flags |= COGL_FEATURE_OFFSCREEN_BLIT; + } + + if (cogl_check_extension ("GL_EXT_framebuffer_multisample", gl_extensions)) + { + ctx->pf_glRenderbufferStorageMultisampleEXT = + (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) + cogl_get_proc_address ("glRenderbufferStorageMultisampleEXT"); + + if (ctx->pf_glRenderbufferStorageMultisampleEXT) + flags |= COGL_FEATURE_OFFSCREEN_MULTISAMPLE; } - return flags; + GE( glGetIntegerv (GL_STENCIL_BITS, &stencil_bits) ); + if (stencil_bits > 0) + flags |= COGL_FEATURE_STENCIL_BUFFER; + + GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); + if (max_clip_planes >= 4) + flags |= COGL_FEATURE_FOUR_CLIP_PLANES; + + /* Cache features */ + ctx->feature_flags = flags; + ctx->features_cached = TRUE; +} + +ClutterFeatureFlags +cogl_get_features () +{ + _COGL_GET_CONTEXT (ctx, 0); + + if (!ctx->features_cached) + _cogl_features_init (); + + return ctx->feature_flags; +} + +gboolean +cogl_features_available (CoglFeatureFlags features) +{ + _COGL_GET_CONTEXT (ctx, 0); + + if (!ctx->features_cached) + _cogl_features_init (); + + return (ctx->feature_flags & features) == features; } void @@ -835,217 +938,79 @@ cogl_fog_set (const ClutterColor *fog_color, glFogf (GL_FOG_END, CLUTTER_FIXED_TO_FLOAT (stop)); } -/* FBOs - offscreen */ - -COGLuint -cogl_offscreen_create (COGLuint target_texture) -{ -#ifdef GL_FRAMEBUFFER_EXT - COGLuint handle; - GLenum status; - - if (_gen_framebuffers == NULL - || _bind_framebuffer == NULL - || _framebuffer_texture_2d == NULL - || _check_framebuffer_status == NULL) - { - /* tmp warning - need error reporting */ - g_warning("Missing GL_FRAMEBUFFER_EXT API\n"); - return 0; - } - - _gen_framebuffers (1, &handle); - _bind_framebuffer (GL_FRAMEBUFFER_EXT, handle); - - _framebuffer_texture_2d (GL_FRAMEBUFFER_EXT, - GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_RECTANGLE_ARB, - target_texture, - 0); - - status = _check_framebuffer_status (GL_FRAMEBUFFER_EXT); - - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) - { - _delete_framebuffers (1, &handle); - return 0; - } - - _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0); - - return handle; -#else - /* tmp warning - need error reporting */ - g_warning("No GL_FRAMEBUFFER_EXT\n"); - return 0; -#endif -} - -void -cogl_offscreen_destroy (COGLuint offscreen_handle) -{ - if (_delete_framebuffers) - _delete_framebuffers (1, &offscreen_handle); -} - -void -cogl_offscreen_redirect_start (COGLuint offscreen_handle, - gint width, - gint height) -{ - /* FIXME: silly we need to pass width / height to init viewport */ -#ifdef GL_FRAMEBUFFER_EXT - - if (_bind_framebuffer == NULL) - return; - - _bind_framebuffer (GL_FRAMEBUFFER_EXT, offscreen_handle); - - glViewport (0, 0, width, height); - - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - glLoadIdentity (); - - glMatrixMode (GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity (); - - glTranslatef (-1.0f, -1.0f, 0.0f); - glScalef (2.0f / (float)width, 2.0f / (float)height, 1.0f); - - /* Clear the scene, appears needed on some backends - OSX */ - glClearColor (0.0, 0.0, 0.0, 0.0); - glClear (GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - -#endif -} - -void -cogl_offscreen_redirect_end (COGLuint offscreen_handle, - gint width, - gint height) -{ - /* FIXME: silly we need to pass width / height to reset to */ - if (_bind_framebuffer == NULL) - return; - -#ifdef GL_FRAMEBUFFER_EXT - glViewport (0, 0, width, height); - - glMatrixMode (GL_PROJECTION); - glPopMatrix(); - - glMatrixMode (GL_MODELVIEW); - glPopMatrix(); - - _bind_framebuffer (GL_FRAMEBUFFER_EXT, 0); -#endif -} - - /* Shader Magic follows */ -#ifdef __GNUC__ - -#define PROC(rettype, retval, procname, args...) \ - static rettype (*proc) (args) = NULL; \ - if (proc == NULL) \ - { \ - proc = (void*)cogl_get_proc_address (#procname);\ - if (!proc)\ - {\ - g_warning ("failed to lookup proc: %s", #procname);\ - return retval;\ - }\ - } -#else - -#define PROC(rettype, retval, procname, ...) \ - static rettype (*proc) (__VA_ARGS__) = NULL; \ - if (proc == NULL) \ - { \ - proc = (void*)cogl_get_proc_address (#procname);\ - if (!proc)\ - {\ - g_warning ("failed to lookup proc: %s", #procname);\ - return retval;\ - }\ - } - -#endif - COGLhandle cogl_create_program (void) { - PROC (GLhandleARB, 0, glCreateProgramObjectARB, void); - return proc (); + _COGL_GET_CONTEXT (ctx, 0); + return glCreateProgramObjectARB (); } COGLhandle cogl_create_shader (COGLenum shaderType) { - PROC (GLhandleARB, 0, glCreateShaderObjectARB, GLenum); - return proc (shaderType); + _COGL_GET_CONTEXT (ctx, 0); + return glCreateShaderObjectARB (shaderType); } void cogl_shader_source (COGLhandle shader, const gchar *source) { - PROC (GLvoid,, glShaderSourceARB, GLhandleARB, GLsizei, const GLcharARB **, const GLint *) - proc (shader, 1, &source, NULL); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glShaderSourceARB (shader, 1, &source, NULL); } void cogl_shader_compile (COGLhandle shader_handle) { - PROC (GLvoid,, glCompileShaderARB, GLhandleARB); - proc (shader_handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glCompileShaderARB (shader_handle); } void cogl_program_attach_shader (COGLhandle program_handle, COGLhandle shader_handle) { - PROC (GLvoid,, glAttachObjectARB, GLhandleARB, GLhandleARB); - proc (program_handle, shader_handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glAttachObjectARB (program_handle, shader_handle); } void cogl_program_link (COGLhandle program_handle) { - PROC (GLvoid,, glLinkProgramARB, GLhandleARB); - proc (program_handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glLinkProgramARB (program_handle); } void cogl_program_use (COGLhandle program_handle) { - PROC (GLvoid,, glUseProgramObjectARB, GLhandleARB); - proc (program_handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glUseProgramObjectARB (program_handle); } COGLint cogl_program_get_uniform_location (COGLhandle program_handle, const gchar *uniform_name) { - PROC (GLint,0, glGetUniformLocationARB, GLhandleARB, const GLcharARB *) - return proc (program_handle, uniform_name); + _COGL_GET_CONTEXT (ctx, 0); + return glGetUniformLocationARB (program_handle, uniform_name); } void cogl_program_destroy (COGLhandle handle) { - PROC (GLvoid,, glDeleteObjectARB, GLhandleARB); - proc (handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glDeleteObjectARB (handle); } void cogl_shader_destroy (COGLhandle handle) { - PROC (GLvoid,, glDeleteObjectARB, GLhandleARB); - proc (handle); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glDeleteObjectARB (handle); } void @@ -1054,8 +1019,8 @@ cogl_shader_get_info_log (COGLhandle handle, gchar *buffer) { COGLint len; - PROC (GLvoid,, glGetInfoLogARB, GLhandleARB, GLsizei, GLsizei *, GLcharARB *); - proc (handle, size-1, &len, buffer); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glGetInfoLogARB (handle, size-1, &len, buffer); buffer[len]='\0'; } @@ -1064,15 +1029,14 @@ cogl_shader_get_parameteriv (COGLhandle handle, COGLenum pname, COGLint *dest) { - PROC (GLvoid,, glGetObjectParameterivARB, GLhandleARB, GLenum, GLint*) - proc (handle, pname, dest); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glGetObjectParameterivARB (handle, pname, dest); } - void cogl_program_uniform_1f (COGLint uniform_no, gfloat value) { - PROC (GLvoid,, glUniform1fARB, GLint, GLfloat); - proc (uniform_no, value); + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + glUniform1fARB (uniform_no, value); } diff --git a/clutter/cogl/gles/Makefile.am b/clutter/cogl/gles/Makefile.am index 32dda4a67..c629e1ee5 100644 --- a/clutter/cogl/gles/Makefile.am +++ b/clutter/cogl/gles/Makefile.am @@ -1,19 +1,36 @@ -libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/clutter -libclutterinclude_HEADERS = $(top_srcdir)/clutter/cogl/cogl.h \ - $(top_srcdir)/clutter/cogl/gles/cogl-defines.h +libclutterincludedir = $(includedir)/clutter-@CLUTTER_API_VERSION@/cogl +libclutterinclude_HEADERS = $(top_builddir)/clutter/cogl/cogl.h \ + $(top_builddir)/clutter/cogl/cogl-defines-gles.h INCLUDES = \ - -I$(top_srcdir) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/clutter \ -I$(top_srcdir)/clutter/cogl \ - $(CLUTTER_CFLAGS) \ - $(CLUTTER_DEBUG_CFLAGS) \ + -I$(top_srcdir)/clutter/cogl/common \ + -I$(top_srcdir)/clutter/cogl/$(CLUTTER_COGL) \ + -I$(top_builddir)/clutter \ + -I$(top_builddir)/clutter/cogl \ + $(CLUTTER_CFLAGS) \ + $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) LDADD = $(CLUTTER_LIBS) noinst_LTLIBRARIES = libclutter-cogl.la -libclutter_cogl_la_SOURCES = \ - $(top_srcdir)/clutter/cogl/cogl.h \ - $(top_srcdir)/clutter/cogl/gl/cogl-defines.h \ - cogl.c +libclutter_cogl_la_SOURCES = \ + $(top_builddir)/clutter/cogl/cogl.h \ + $(top_builddir)/clutter/cogl/gles/cogl-defines.h \ + cogl-defines.h \ + cogl-internal.h \ + cogl-primitives.h \ + cogl-texture.h \ + cogl-fbo.h \ + cogl-context.h \ + cogl.c \ + cogl-primitives.c \ + cogl-texture.c \ + cogl-fbo.c \ + cogl-context.c + +libclutter_cogl_la_LIBADD = $(top_builddir)/clutter/cogl/common/libclutter-cogl-common.la diff --git a/clutter/cogl/gles/cogl-context.c b/clutter/cogl/gles/cogl-context.c new file mode 100644 index 000000000..0027e96df --- /dev/null +++ b/clutter/cogl/gles/cogl-context.c @@ -0,0 +1,96 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" + +#include +#include + +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" + +static CoglContext *_context = NULL; + +gboolean +cogl_create_context () +{ + if (_context != NULL) + return FALSE; + + /* Allocate context memory */ + _context = (CoglContext*) g_malloc (sizeof (CoglContext)); + + /* Init default values */ + _context->feature_flags = 0; + _context->features_cached = FALSE; + + _context->enable_flags = 0; + _context->color_alpha = 255; + + _context->path_nodes = NULL; + _context->path_nodes_cap = 0; + _context->path_nodes_size = 0; + + _context->texture_handles = NULL; + _context->texture_vertices_size = 0; + _context->texture_vertices = NULL; + + _context->fbo_handles = NULL; + _context->draw_buffer = COGL_WINDOW_BUFFER; + + /* Init OpenGL state */ + glTexEnvx (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColorMask (TRUE, TRUE, TRUE, FALSE); + cogl_enable (0); + + return TRUE; +} + +void +cogl_destroy_context () +{ + if (_context == NULL) + return; + + if (_context->texture_vertices) + g_free (_context->texture_vertices); + + g_free (_context); +} + +CoglContext * +_cogl_context_get_default () +{ + /* Create if doesn't exists yet */ + if (_context == NULL) + cogl_create_context (); + + return _context; +} diff --git a/clutter/cogl/gles/cogl-context.h b/clutter/cogl/gles/cogl-context.h new file mode 100644 index 000000000..c5117a9f5 --- /dev/null +++ b/clutter/cogl/gles/cogl-context.h @@ -0,0 +1,78 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_CONTEXT_H +#define __COGL_CONTEXT_H + +#include "cogl-primitives.h" + +typedef struct +{ + GLfixed v[3]; + GLfixed t[2]; + GLfixed c[4]; +} CoglTextureGLVertex; + +typedef struct +{ + /* Features cache */ + CoglFeatureFlags feature_flags; + gboolean features_cached; + + /* Enable cache */ + gulong enable_flags; + guint8 color_alpha; + + /* Primitives */ + CoglFixedVec2 path_start; + CoglFixedVec2 path_pen; + CoglFixedVec2 *path_nodes; + guint path_nodes_cap; + guint path_nodes_size; + CoglFixedVec2 path_nodes_min; + CoglFixedVec2 path_nodes_max; + + /* Textures */ + GArray *texture_handles; + CoglTextureGLVertex *texture_vertices; + gulong texture_vertices_size; + + /* Framebuffer objects */ + GArray *fbo_handles; + CoglBufferTarget draw_buffer; + +} CoglContext; + +CoglContext * +_cogl_context_get_default (); + +/* Obtains the context and returns retval if NULL */ +#define _COGL_GET_CONTEXT(ctxvar, retval) \ +CoglContext *ctxvar = _cogl_context_get_default (); \ +if (ctxvar == NULL) return retval; + +#define NO_RETVAL + +#endif /* __COGL_CONTEXT_H */ diff --git a/clutter/cogl/gles/cogl-defines.h b/clutter/cogl/gles/cogl-defines.h.in similarity index 98% rename from clutter/cogl/gles/cogl-defines.h rename to clutter/cogl/gles/cogl-defines.h.in index 83f2ae15d..b304680f9 100644 --- a/clutter/cogl/gles/cogl-defines.h +++ b/clutter/cogl/gles/cogl-defines.h.in @@ -444,18 +444,6 @@ typedef GLuint COGLhandle; /* extras */ -#define CGL_TEXTURE_2D GL_TEXTURE_2D -#define CGL_ARGB GL_ARGB - -/* FIXME: There is no BGR support in GLES - so with below BGR textures are - * borked. Will likely need a feature flag and some coversion.. - */ -#define CGL_BGR GL_RGB -#define CGL_BGRA GL_RGBA - -/* Its unlikely we support this */ -#define CGL_TEXTURE_RECTANGLE_ARB 0 - /* YUV textures also unsupported */ #define CGL_YCBCR_MESA 0 #define CGL_UNSIGNED_SHORT_8_8_REV_MESA 0 diff --git a/clutter/cogl/gles/cogl-fbo.c b/clutter/cogl/gles/cogl-fbo.c new file mode 100644 index 000000000..e8f186b31 --- /dev/null +++ b/clutter/cogl/gles/cogl-fbo.c @@ -0,0 +1,188 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture.h" +#include "cogl-fbo.h" +#include "cogl-context.h" + +/* Expecting EXT functions not to be defined - redirect to pointers in context */ +#define glGenRenderbuffersEXT ctx->pf_glGenRenderbuffersEXT +#define glBindRenderbufferEXT ctx->pf_glBindRenderbufferEXT +#define glRenderbufferStorageEXT ctx->pf_glRenderbufferStorageEXT +#define glGenFramebuffersEXT ctx->pf_glGenFramebuffersEXT +#define glBindFramebufferEXT ctx->pf_glBindFramebufferEXT +#define glFramebufferTexture2DEXT ctx->pf_glFramebufferTexture2DEXT +#define glFramebufferRenderbufferEXT ctx->pf_glFramebufferRenderbufferEXT +#define glCheckFramebufferStatusEXT ctx->pf_glCheckFramebufferStatusEXT +#define glDeleteFramebuffersEXT ctx->pf_glDeleteFramebuffersEXT +#define glBlitFramebufferEXT ctx->pf_glBlitFramebufferEXT +#define glRenderbufferStorageMultisampleEXT ctx->pf_glRenderbufferStorageMultisampleEXT + +static gint +_cogl_fbo_handle_find (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, -1); + + gint i; + + if (ctx->fbo_handles == NULL) + return -1; + + for (i=0; i < ctx->fbo_handles->len; ++i) + if (g_array_index (ctx->fbo_handles, CoglHandle, i) == handle) + return i; + + return -1; +} +/* +static CoglHandle +_cogl_fbo_handle_new (CoglFbo *fbo) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + CoglHandle handle = (CoglHandle)fbo; + + if (ctx->fbo_handles == NULL) + ctx->fbo_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); + + g_array_append_val (ctx->fbo_handles, handle); + + return handle; +} + +static void +_cogl_fbo_handle_release (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + gint i; + + if ( (i = _cogl_fbo_handle_find (handle)) == -1) + return; + + g_array_remove_index_fast (ctx->fbo_handles, i); +} + +static CoglFbo* +_cogl_fbo_pointer_from_handle (CoglHandle handle) +{ + return (CoglFbo*) handle; +}*/ + +gboolean +cogl_is_offscreen_buffer (CoglHandle handle) +{ + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return _cogl_fbo_handle_find (handle) >= 0; +} + +CoglHandle +cogl_offscreen_new_to_texture (CoglHandle texhandle) +{ + return COGL_INVALID_HANDLE; +} + +CoglHandle +cogl_offscreen_new_multisample () +{ + return COGL_INVALID_HANDLE; +} + +CoglHandle +cogl_offscreen_ref (CoglHandle handle) +{ + return COGL_INVALID_HANDLE; +} + +void +cogl_offscreen_unref (CoglHandle handle) +{ +} + +void +cogl_offscreen_blit_region (CoglHandle src_buffer, + CoglHandle dst_buffer, + int src_x, + int src_y, + int src_w, + int src_h, + int dst_x, + int dst_y, + int dst_w, + int dst_h) +{ +} + +void +cogl_offscreen_blit (CoglHandle src_buffer, + CoglHandle dst_buffer) +{ +} + +void +cogl_draw_buffer (CoglBufferTarget target, CoglHandle offscreen) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + switch (target) + { + case COGL_OFFSCREEN_BUFFER: + + /* Not supported */ + return; + + case COGL_WINDOW_BUFFER: + + /* Draw to RGB channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE) ); + + break; + + case COGL_MASK_BUFFER: + + /* Draw only to ALPHA channel */ + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE) ); + + break; + case COGL_WINDOW_BUFFER & COGL_MASK_BUFFER: + + /* Draw to all channels */ + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + break; + } + + /* Store new target */ + ctx->draw_buffer = target; +} diff --git a/clutter/cogl/gles/cogl-fbo.h b/clutter/cogl/gles/cogl-fbo.h new file mode 100644 index 000000000..f2dd5298f --- /dev/null +++ b/clutter/cogl/gles/cogl-fbo.h @@ -0,0 +1,38 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_FBO_H +#define __COGL_FBO_H + +typedef struct +{ + guint ref_count; + int width; + int height; + GLuint gl_handle; + +} CoglFbo; + +#endif /* __COGL_FBO_H */ diff --git a/clutter/cogl/gles/cogl-internal.h b/clutter/cogl/gles/cogl-internal.h new file mode 100644 index 000000000..69baa4a9a --- /dev/null +++ b/clutter/cogl/gles/cogl-internal.h @@ -0,0 +1,63 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_INTERNAL_H +#define __COGL_INTERNAL_H + +#define COGL_DEBUG 0 + +#if COGL_DEBUG +#define GE(x...) { \ + GLenum err; \ + (x); \ + fprintf(stderr, "%s\n", #x); \ + while ((err = glGetError()) != GL_NO_ERROR) { \ + fprintf(stderr, "glError: %s caught at %s:%u\n", \ + (char *)error_string(err), \ + __FILE__, __LINE__); \ + } \ +} +#else +#define GE(x) (x); +#endif + +#define COGL_ENABLE_BLEND (1<<1) +#define COGL_ENABLE_TEXTURE_2D (1<<2) +#define COGL_ENABLE_ALPHA_TEST (1<<3) +#define COGL_ENABLE_TEXTURE_RECT (1<<4) +#define COGL_ENABLE_VERTEX_ARRAY (1<<5) +#define COGL_ENABLE_TEXCOORD_ARRAY (1<<6) +#define COGL_ENABLE_COLOR_ARRAY (1<<7) + +gint +_cogl_get_format_bpp (CoglPixelFormat format); + +void +cogl_enable (gulong flags); + +gulong +cogl_get_enable (); + +#endif /* __COGL_INTERNAL_H */ diff --git a/clutter/cogl/gles/cogl-primitives.c b/clutter/cogl/gles/cogl-primitives.c new file mode 100644 index 000000000..1bba58f55 --- /dev/null +++ b/clutter/cogl/gles/cogl-primitives.c @@ -0,0 +1,1112 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-context.h" + +#include +#include + +#define _COGL_MAX_BEZ_RECURSE_DEPTH 16 + +/** + * SECTION:cogl-primitives + * @short_description: Functions that draw various primitive shapes and + * allow for construction of more complex paths. + * + * There are three levels on which drawing with cogl can be used. The + * highest level functions construct various simple primitive shapes + * to be either filled or stroked. Using a lower-level set of functions + * more complex and arbitrary paths can be constructed by concatenating + * straight line, bezier curve and arc segments. Additionally there + * are utility functions that draw the most common primitives - rectangles + * and trapezoids - in a maximaly optimized fashion. + * + * When constructing arbitrary paths, the current pen location is + * initialized using the move_to command. The subsequent path segments + * implicitly use the last pen location as their first vertex and move + * the pen location to the last vertex they produce at the end. Also + * there are special versions of functions that allow specifying the + * vertices of the path segments relative to the last pen location + * rather then in the absolute coordinates. + */ + +/** + * cogl_fast_fill_rectangle: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * + * Fills a rectangle at the given coordinates with the current + * drawing color in a highly optimizied fashion. + **/ +void +cogl_fast_fill_rectangle (gint x, + gint y, + guint width, + guint height) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* 32-bit integers are not supported as coord types + in GLES . Fixed type has got 16 bits left of the + point which is equal to short anyway. */ + + GLshort rect_verts[8] = { + (GLshort) x, (GLshort) y, + (GLshort) (x + width), (GLshort) y, + (GLshort) x, (GLshort) (y + height), + (GLshort) (x + width), (GLshort) (y + height) + }; + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_SHORT, 0, rect_verts) ); + GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +/** + * cogl_fast_fill_rectanglex: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * + * A fixed-point version of cogl_fast_fill_rectangle. + **/ +void +cogl_fast_fill_rectanglex (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GLfixed rect_verts[8] = { + x, y, + x + width, y, + x, y + height, + x + width, y + height + }; + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FIXED, 0, rect_verts) ); + GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +/** + * cogl_fast_fill_trapezoid: + * @y1: Y coordinate of the top two vertices. + * @x11: X coordinate of the top-left vertex. + * @x21: X coordinate of the top-right vertex. + * @y2: Y coordinate of the bottom two vertices. + * @x12: X coordinate of the bottom-left vertex. + * @x22: X coordinate of the bottom-right vertex. + * + * Fills a trapezoid at the given coordinates with the current + * drawing color in a highly optimized fashion. + **/ +void +cogl_fast_fill_trapezoid (gint y1, + gint x11, + gint x21, + gint y2, + gint x12, + gint x22) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GLshort trap_verts[8] = { + (GLshort) x11, (GLshort) y1, + (GLshort) x21, (GLshort) y1, + (GLshort) x12, (GLshort) y2, + (GLshort) x22, (GLshort) y2 + }; + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_SHORT, 0, trap_verts) ); + GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +/** + * cogl_fast_fill_trapezoidx: + * @y1: Y coordinate of the top two vertices. + * @x11: X coordinate of the top-left vertex. + * @x21: X coordinate of the top-right vertex. + * @y2: Y coordinate of the bottom two vertices. + * @x12: X coordinate of the bottom-left vertex. + * @x22: X coordinate of the bottom-right vertex. + * + * A fixed-point version of cogl_fast_fill_trapezoid. + **/ +void +cogl_fast_fill_trapezoidx (ClutterFixed y1, + ClutterFixed x11, + ClutterFixed x21, + ClutterFixed y2, + ClutterFixed x12, + ClutterFixed x22) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GLfixed trap_verts[8] = { + x11, y1, + x21, y1, + x12, y2, + x22, y2 + }; + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FIXED, 0, trap_verts) ); + GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +static void +_cogl_path_clear_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes) + g_free (ctx->path_nodes); + + ctx->path_nodes = (CoglFixedVec2*) g_malloc (2 * sizeof(CoglFixedVec2)); + ctx->path_nodes_size = 0; + ctx->path_nodes_cap = 2; +} + +static void +_cogl_path_add_node (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglFixedVec2 *new_nodes = NULL; + + if (ctx->path_nodes_size == ctx->path_nodes_cap) + { + new_nodes = g_realloc (ctx->path_nodes, + 2 * ctx->path_nodes_cap + * sizeof (CoglFixedVec2)); + + if (new_nodes == NULL) return; + + ctx->path_nodes = new_nodes; + ctx->path_nodes_cap *= 2; + } + + ctx->path_nodes [ctx->path_nodes_size] .x = x; + ctx->path_nodes [ctx->path_nodes_size] .y = y; + ctx->path_nodes_size++; + + if (ctx->path_nodes_size == 1) + { + ctx->path_nodes_min.x = ctx->path_nodes_max.x = x; + ctx->path_nodes_min.y = ctx->path_nodes_max.y = y; + } + else + { + if (x < ctx->path_nodes_min.x) ctx->path_nodes_min.x = x; + if (x > ctx->path_nodes_max.x) ctx->path_nodes_max.x = x; + if (y < ctx->path_nodes_min.y) ctx->path_nodes_min.y = y; + if (y > ctx->path_nodes_max.y) ctx->path_nodes_max.y = y; + } +} + +static void +_cogl_path_stroke_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) ); + GE( glDrawArrays (GL_LINE_STRIP, 0, ctx->path_nodes_size) ); +} + +static void +_cogl_path_fill_nodes () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + guint bounds_x; + guint bounds_y; + guint bounds_w; + guint bounds_h; + + GE( glClear (GL_STENCIL_BUFFER_BIT) ); + + GE( glEnable (GL_STENCIL_TEST) ); + GE( glStencilFunc (GL_ALWAYS, 0x0, 0x0) ); + GE( glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT) ); + GE( glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE) ); + + cogl_enable (COGL_ENABLE_VERTEX_ARRAY + | (ctx->color_alpha < 255 + ? COGL_ENABLE_BLEND : 0)); + + GE( glVertexPointer (2, GL_FIXED, 0, ctx->path_nodes) ); + GE( glDrawArrays (GL_TRIANGLE_FAN, 0, ctx->path_nodes_size) ); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) ); + GE( glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) ); + + bounds_x = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.x); + bounds_y = CLUTTER_FIXED_FLOOR (ctx->path_nodes_min.y); + bounds_w = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.x - ctx->path_nodes_min.x); + bounds_h = CLUTTER_FIXED_CEIL (ctx->path_nodes_max.y - ctx->path_nodes_min.y); + + cogl_fast_fill_rectangle (bounds_x, bounds_y, bounds_w, bounds_h); + + GE( glDisable (GL_STENCIL_TEST) ); +} + +/** + * cogl_fill: + * + * Fills the constructed shape using the current drawing color. + **/ +void +cogl_fill () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes_size == 0) + return; + + _cogl_path_fill_nodes(); +} + +/** + * cogl_stroke: + * + * Strokes the constructed shape using the current drawing color + * and a width of 1 pixel (regardless of the current transformation + * matrix). + **/ +void +cogl_stroke () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (ctx->path_nodes_size == 0) + return; + + _cogl_path_stroke_nodes(); +} + +/** + * cogl_path_move_to: + * @x: X coordinate of the pen location to move to. + * @y: Y coordinate of the pen location to move to. + * + * Clears the previously constructed shape and begins a new path + * contour by moving the pen to the given coordinates. + **/ +void +cogl_path_move_to (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* FIXME: handle multiple contours maybe? */ + + _cogl_path_clear_nodes (); + _cogl_path_add_node (x, y); + + ctx->path_start.x = x; + ctx->path_start.y = y; + + ctx->path_pen = ctx->path_start; +} + +/** + * cogl_path_move_to_rel: + * @x: X offset from the current pen location to move the pen to. + * @y: Y offset from the current pen location to move the pen to. + * + * Clears the previously constructed shape and begins a new path + * contour by moving the pen to the given coordinates relative + * to the current pen location. + **/ +void +cogl_path_move_to_rel (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_move_to (ctx->path_pen.x + x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_line_to: + * @x: X coordinate of the end line vertex + * @y: Y coordinate of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates. + **/ +void +cogl_path_line_to (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_path_add_node (x, y); + + ctx->path_pen.x = x; + ctx->path_pen.y = y; +} + +/** + * cogl_path_line_to: + * @x: X offset from the current pen location of the end line vertex + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a straight line segment to the current path that ends at the + * given coordinates relative to the current pen location. + **/ +void +cogl_path_line_to_rel (ClutterFixed x, + ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x + x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_h_line_to: + * @x: X coordinate of the end line vertex + * + * Adds a straight horizontal line segment to the current path that + * ends at the given X coordinate and current pen Y coordinate. + **/ +void +cogl_path_h_line_to (ClutterFixed x) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (x, + ctx->path_pen.y); +} + +/** + * cogl_path_v_line_to: + * @y: Y coordinate of the end line vertex + * + * Adds a stright vertical line segment to the current path that ends + * at the current pen X coordinate and the given Y coordinate. + **/ +void +cogl_path_v_line_to (ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x, + y); +} + +/** + * cogl_path_h_line_to_rel: + * @x: X offset from the current pen location of the end line vertex + * + * Adds a straight horizontal line segment to the current path that + * ends at the given X coordinate relative to the current pen location + * and current pen Y coordinate. + **/ +void +cogl_path_h_line_to_rel (ClutterFixed x) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x + x, + ctx->path_pen.y); +} + +/** + * cogl_path_v_line_to_rel: + * @y: Y offset from the current pen location of the end line vertex + * + * Adds a stright vertical line segment to the current path that ends + * at the current pen X coordinate and the given Y coordinate relative + * to the current pen location. + **/ +void +cogl_path_v_line_to_rel (ClutterFixed y) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_line_to (ctx->path_pen.x, + ctx->path_pen.y + y); +} + +/** + * cogl_path_close: + * + * Closes the path being constructed by adding a straight line segment + * to it that ends at the first vertex of the path. + **/ +void +cogl_path_close () +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_path_add_node (ctx->path_start.x, ctx->path_start.y); + ctx->path_pen = ctx->path_start; +} + +/** + * cogl_line: + * @x1: X coordinate of the start line vertex + * @y1: Y coordinate of the start line vertex + * @x2: X coordinate of the end line vertex + * @y2: Y coordinate of the end line vertex + * + * Clears the previously constructed shape and constructs a straight + * line shape start and ending at the given coordinates. + **/ +void +cogl_line (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + cogl_path_move_to (x1, y1); + cogl_path_line_to (x2, y2); +} + +/** + * cogl_polyline: + * @coords: A pointer to the first element of an array of fixed-point + * values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Clears the previously constructed shape and constructs a series of straight + * line segments, starting from the first given vertex coordinate. Each + * subsequent segment stars where the previous one ended and ends at the next + * given vertex coordinate. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. (num_points - 1) segments will + * be constructed. + **/ +void +cogl_polyline (ClutterFixed *coords, + gint num_points) +{ + gint c = 0; + + cogl_path_move_to(coords[0], coords[1]); + + for (c = 1; c < num_points; ++c) + cogl_path_line_to(coords[2*c], coords[2*c+1]); +} + +/** + * cogl_polygon: + * @coords: A pointer to the first element of an array of fixed-point + * values that specify the vertex coordinates. + * @num_points: The total number of vertices. + * + * Clears the previously constructed shape and constructs a polygonal + * shape of the given number of vertices. + * + * The coords array must contain 2 * num_points values. The first value + * represents the X coordinate of the first vertex, the second value + * represents the Y coordinate of the first vertex, continuing in the same + * fashion for the rest of the vertices. + **/ +void +cogl_polygon (ClutterFixed *coords, + gint num_points) +{ + cogl_polyline (coords, num_points); + cogl_path_close (); +} + +/** + * cogl_rectangle: + * @x: X coordinate of the top-left corner. + * @y: Y coordinate of the top-left corner. + * @width: Rectangle width. + * @height: Rectangle height. + * + * Clears the previously constructed shape and constructs a rectangular + * shape at the given coordinates. + **/ +void +cogl_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height) +{ + cogl_path_move_to (x, y); + cogl_path_line_to (x + width, y); + cogl_path_line_to (x + width, y + height); + cogl_path_line_to (x, y + height); + cogl_path_close (); +} + +static void +_cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step, + guint move_first) +{ + ClutterAngle a = 0x0; + ClutterAngle temp = 0x0; + ClutterFixed cosa = 0x0; + ClutterFixed sina = 0x0; + ClutterFixed px = 0x0; + ClutterFixed py = 0x0; + + /* Fix invalid angles */ + + if (angle_1 == angle_2 || angle_step == 0x0) + return; + + if (angle_step < 0x0) + angle_step = -angle_step; + + if (angle_2 < angle_1) + { + temp = angle_1; + angle_1 = angle_2; + angle_2 = temp; + } + + /* Walk the arc by given step */ + + for (a = angle_1; a < angle_2; a += angle_step) + { + cosa = clutter_cosi (a); + sina = clutter_sini (a); + + px = center_x + CFX_MUL (cosa, radius_x); + py = center_y + CFX_MUL (sina, radius_y); + + if (a == angle_1 && move_first) + cogl_path_move_to (px, py); + else + cogl_path_line_to (px, py); + } +} + +/** + * cogl_path_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. + **/ +void +cogl_path_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _cogl_arc (center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +/** + * cogl_path_arc_rel: + * @center_x: X offset from the current pen location of the elliptical + * arc center + * @center_y: Y offset from the current pen location of the elliptical + * arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Adds an elliptical arc segment to the current path. A straight line + * segment will link the current pen location with the first vertex + * of the arc. + **/ +void +cogl_path_arc_rel (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_arc (ctx->path_pen.x + center_x, + ctx->path_pen.y + center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 0 /* no move */); +} + +/** + * cogl_arc: + * @center_x: X coordinate of the elliptical arc center + * @center_y: Y coordinate of the elliptical arc center + * @radius_x: X radius of the elliptical arc + * @radius_y: Y radious of the elliptical arc + * @angle_1: Angle in the unit-circle at which the arc begin + * @angle_2: Angle in the unit-circle at which the arc ends + * @angle_step: Angle increment resolution for subdivision + * + * Clears the previously constructed shape and constructs and elliptical arc + * shape. + **/ +void +cogl_arc (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_1, + ClutterAngle angle_2, + ClutterAngle angle_step) +{ + _cogl_arc (center_x, center_y, + radius_x, radius_y, + angle_1, angle_2, + angle_step, 1 /* move first */); +} + +/** + * cogl_ellipse: + * @center_x: X coordinate of the ellipse center + * @center_y: Y coordinate of the ellipse center + * @radius_x: X radius of the ellipse + * @radius_y: Y radius of the ellipse + * @angle_step: Angle increment resolution for subdivision + * + * Clears the previously constructed shape and constructs an ellipse + * shape. + **/ +void +cogl_ellipse (ClutterFixed center_x, + ClutterFixed center_y, + ClutterFixed radius_x, + ClutterFixed radius_y, + ClutterAngle angle_step) +{ + + /* FIXME: if shows to be slow might be optimized + * by mirroring just a quarter of it */ + + _cogl_arc (center_x, center_y, + radius_x, radius_y, + 0, CLUTTER_ANGLE_FROM_DEG(360), + angle_step, 1 /* move first */); + + cogl_path_close(); +} + +/** + * cogl_round_rectangle: + * @x: X coordinate of the top-left corner + * @y: Y coordinate of the top-left corner + * @width: Width of the rectangle + * @height: Height of the rectangle + * @radius: Radius of the corner arcs. + * @arc_step: Angle increment resolution for subdivision of + * the corner arcs. + * + * Clears the previously constructed shape and constructs a rectangular + * shape with rounded corners. + **/ +void +cogl_round_rectangle (ClutterFixed x, + ClutterFixed y, + ClutterFixed width, + ClutterFixed height, + ClutterFixed radius, + ClutterAngle arc_step) +{ + ClutterFixed inner_width = width - (radius << 1); + ClutterFixed inner_height = height - (radius << 1); + + cogl_path_move_to (x, y + radius); + cogl_path_arc_rel (radius, 0, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (180), + CLUTTER_ANGLE_FROM_DEG (270), + arc_step); + + cogl_path_h_line_to_rel (inner_width); + cogl_path_arc_rel (0, radius, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (-90), + CLUTTER_ANGLE_FROM_DEG (0), + arc_step); + + cogl_path_v_line_to_rel (inner_height); + cogl_path_arc_rel (-radius, 0, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (0), + CLUTTER_ANGLE_FROM_DEG (90), + arc_step); + + cogl_path_h_line_to_rel (-inner_width); + cogl_path_arc_rel (0, -radius, + radius, radius, + CLUTTER_ANGLE_FROM_DEG (90), + CLUTTER_ANGLE_FROM_DEG (180), + arc_step); + + cogl_path_close (); +} + +static void +_cogl_path_bezier2_sub (CoglBezQuad *quad) +{ + CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezQuad *qleft; + CoglBezQuad *qright; + CoglBezQuad *q; + CoglFixedVec2 mid; + CoglFixedVec2 dif; + CoglFixedVec2 c1; + CoglFixedVec2 c2; + CoglFixedVec2 c3; + gint qindex; + + /* Put first curve on stack */ + quads[0] = *quad; + qindex = 0; + + /* While stack is not empty */ + while (qindex >= 0) + { + + q = &quads[qindex]; + + /* Calculate distance of control point from its + * counterpart on the line between end points */ + mid.x = ((q->p1.x + q->p3.x) >> 1); + mid.y = ((q->p1.y + q->p3.y) >> 1); + dif.x = (q->p2.x - mid.x); + dif.y = (q->p2.y - mid.y); + if (dif.x < 0) dif.x = -dif.x; + if (dif.y < 0) dif.y = -dif.y; + + /* Cancel if the curve is flat enough */ + if (dif.x + dif.y <= CFX_ONE + || qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1) + { + /* Add subdivision point (skip last) */ + if (qindex == 0) return; + _cogl_path_add_node (q->p3.x, q->p3.y); + --qindex; continue; + } + + /* Left recursion goes on top of stack! */ + qright = q; qleft = &quads[++qindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((q->p1.x + q->p2.x) >> 1); + c1.y = ((q->p1.y + q->p2.y) >> 1); + c3.x = ((q->p2.x + q->p3.x) >> 1); + c3.y = ((q->p2.y + q->p3.y) >> 1); + c2.x = ((c1.x + c3.x) >> 1); + c2.y = ((c1.y + c3.y) >> 1); + + /* Add left recursion onto stack */ + qleft->p1 = q->p1; + qleft->p2 = c1; + qleft->p3 = c2; + + /* Add right recursion onto stack */ + qright->p1 = c2; + qright->p2 = c3; + qright->p3 = q->p3; + } +} + +static void +_cogl_path_bezier3_sub (CoglBezCubic *cubic) +{ + CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH]; + CoglBezCubic *cleft; + CoglBezCubic *cright; + CoglBezCubic *c; + CoglFixedVec2 dif1; + CoglFixedVec2 dif2; + CoglFixedVec2 mm; + CoglFixedVec2 c1; + CoglFixedVec2 c2; + CoglFixedVec2 c3; + CoglFixedVec2 c4; + CoglFixedVec2 c5; + gint cindex; + + /* Put first curve on stack */ + cubics[0] = *cubic; + cindex = 0; + + while (cindex >= 0) + { + c = &cubics[cindex]; + +#define CFX_MUL2(x) ((x) << 1) +#define CFX_MUL3(x) (((x) << 1) + (x)) +#define CFX_SQ(x) CFX_MUL (x, x) + + /* Calculate distance of control points from their + * counterparts on the line between end points */ + dif1.x = CFX_MUL3 (c->p2.x) - CFX_MUL2 (c->p1.x) - c->p4.x; + dif1.y = CFX_MUL3 (c->p2.y) - CFX_MUL2 (c->p1.y) - c->p4.y; + dif2.x = CFX_MUL3 (c->p3.x) - CFX_MUL2 (c->p4.x) - c->p1.x; + dif2.y = CFX_MUL3 (c->p3.y) - CFX_MUL2 (c->p4.y) - c->p1.y; + if (dif1.x < 0) dif1.x = -dif1.x; + if (dif1.y < 0) dif1.y = -dif1.y; + if (dif2.x < 0) dif2.x = -dif2.x; + if (dif2.y < 0) dif2.y = -dif2.y; + +#undef CFX_MUL2 +#undef CFX_MUL3 +#undef CFX_SQ + + /* Pick the greatest of two distances */ + if (dif1.x < dif2.x) dif1.x = dif2.x; + if (dif1.y < dif2.y) dif1.y = dif2.y; + + /* Cancel if the curve is flat enough */ + if (dif1.x + dif1.y <= CFX_ONE + || cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1) + { + /* Add subdivision point (skip last) */ + if (cindex == 0) return; + _cogl_path_add_node (c->p4.x, c->p4.y); + --cindex; continue; + } + + /* Left recursion goes on top of stack! */ + cright = c; cleft = &cubics[++cindex]; + + /* Subdivide into 2 sub-curves */ + c1.x = ((c->p1.x + c->p2.x) >> 1); + c1.y = ((c->p1.y + c->p2.y) >> 1); + mm.x = ((c->p2.x + c->p3.x) >> 1); + mm.y = ((c->p2.y + c->p3.y) >> 1); + c5.x = ((c->p3.x + c->p4.x) >> 1); + c5.y = ((c->p3.y + c->p4.y) >> 1); + + c2.x = ((c1.x + mm.x) >> 1); + c2.y = ((c1.y + mm.y) >> 1); + c4.x = ((mm.x + c5.x) >> 1); + c4.y = ((mm.y + c5.y) >> 1); + + c3.x = ((c2.x + c4.x) >> 1); + c3.y = ((c2.y + c4.y) >> 1); + + /* Add left recursion to stack */ + cleft->p1 = c->p1; + cleft->p2 = c1; + cleft->p3 = c2; + cleft->p4 = c3; + + /* Add right recursion to stack */ + cright->p1 = c3; + cright->p2 = c4; + cright->p3 = c5; + cright->p4 = c->p4; + } +} + +/** + * cogl_path_bezier2_to: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * + * Adds a quadratic bezier curve segment to the current path with the given + * second and third control points and using current pen location as the + * first control point. + **/ +void +cogl_path_bezier2_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglBezQuad quad; + + /* Prepare quadratic curve */ + quad.p1 = ctx->path_pen; + quad.p2.x = x1; + quad.p2.y = y1; + quad.p3.x = x2; + quad.p3.y = y2; + + /* Run subdivision */ + _cogl_path_bezier2_sub (&quad); + + /* Add last point */ + _cogl_path_add_node (quad.p3.x, quad.p3.y); + ctx->path_pen = quad.p3; +} + +/** + * cogl_path_bezier3_to: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * @x3: X coordinate of the fourth bezier control point + * @y3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. + **/ +void +cogl_path_bezier3_to (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglBezCubic cubic; + + /* Prepare cubic curve */ + cubic.p1 = ctx->path_pen; + cubic.p2.x = x1; + cubic.p2.y = y1; + cubic.p3.x = x2; + cubic.p3.y = y2; + cubic.p4.x = x3; + cubic.p4.y = y3; + + /* Run subdivision */ + _cogl_path_bezier3_sub (&cubic); + + /* Add last point */ + _cogl_path_add_node (cubic.p4.x, cubic.p4.y); + ctx->path_pen = cubic.p4; +} + +/** + * cogl_path_bezier2_to_rel: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * + * Adds a quadratic bezier curve segment to the current path with the given + * second and third control points and using current pen location as the + * first control point. The given coordinates are relative to the current + * pen location. + **/ +void +cogl_path_bezier2_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_bezier2_to (ctx->path_pen.x + x1, + ctx->path_pen.y + y2, + ctx->path_pen.x + x2, + ctx->path_pen.y + y2); +} + +/** + * cogl_path_bezier3_to_rel: + * @x1: X coordinate of the second bezier control point + * @y1: Y coordinate of the second bezier control point + * @x2: X coordinate of the third bezier control point + * @y2: Y coordinate of the third bezier control point + * @x3: X coordinate of the fourth bezier control point + * @y3: Y coordinate of the fourth bezier control point + * + * Adds a cubic bezier curve segment to the current path with the given + * second, third and fourth control points and using current pen location + * as the first control point. The given coordinates are relative to the + * current pen location. + **/ +void +cogl_path_bezier3_to_rel (ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed x3, + ClutterFixed y3) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + cogl_path_bezier3_to (ctx->path_pen.x + x1, + ctx->path_pen.y + y2, + ctx->path_pen.x + x2, + ctx->path_pen.y + y2, + ctx->path_pen.x + x3, + ctx->path_pen.y + y3); +} diff --git a/clutter/cogl/gles/cogl-primitives.h b/clutter/cogl/gles/cogl-primitives.h new file mode 100644 index 000000000..4c96f409b --- /dev/null +++ b/clutter/cogl/gles/cogl-primitives.h @@ -0,0 +1,54 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_PRIMITIVES_H +#define __COGL_PRIMITIVES_H + +typedef struct _CoglFixedVec2 CoglFixedVec2; +typedef struct _CoglBezQuad CoglBezQuad; +typedef struct _CoglBezCubic CoglBezCubic; + +struct _CoglFixedVec2 +{ + ClutterFixed x; + ClutterFixed y; +}; + +struct _CoglBezQuad +{ + CoglFixedVec2 p1; + CoglFixedVec2 p2; + CoglFixedVec2 p3; +}; + +struct _CoglBezCubic +{ + CoglFixedVec2 p1; + CoglFixedVec2 p2; + CoglFixedVec2 p3; + CoglFixedVec2 p4; +}; + +#endif /* __COGL_PRIMITIVES_H */ diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c new file mode 100644 index 000000000..2eae2c9b7 --- /dev/null +++ b/clutter/cogl/gles/cogl-texture.c @@ -0,0 +1,2185 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-bitmap.h" +#include "cogl-texture.h" +#include "cogl-context.h" + +#include +#include +/* +#define COGL_DEBUG 1 + +#define GE(x) \ +{ \ + glGetError(); x; \ + GLuint err = glGetError(); \ + if (err != 0) \ + printf("err: 0x%x\n", err); \ +} */ + +struct _CoglSpanIter +{ + gint index; + GArray *array; + CoglTexSliceSpan *span; + ClutterFixed pos; + ClutterFixed next_pos; + ClutterFixed origin; + ClutterFixed cover_start; + ClutterFixed cover_end; + ClutterFixed intersect_start; + ClutterFixed intersect_end; + ClutterFixed intersect_start_local; + ClutterFixed intersect_end_local; + gboolean intersects; +}; + +/* + * _cogl_texture_handle_find: + * @handle: A texture handle + * + * Returns the index of the given CoglHandle if found in the + * handle array. + */ +static gint +_cogl_texture_handle_find (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, -1); + + gint i; + + if (ctx->texture_handles == NULL) + return -1; + + for (i=0; i < ctx->texture_handles->len; ++i) + if (g_array_index (ctx->texture_handles, CoglHandle, i) == handle) + return i; + + return -1; +} + +/* + * _cogl_texture_handle_new: + * @tex: A pointer to an allocated CoglTexture structure + * + * Returns a new CoglHandle for the given CoglTexture + * object. + */ +static CoglHandle +_cogl_texture_handle_new (CoglTexture *tex) +{ + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + CoglHandle handle = (CoglHandle)tex; + + if (ctx->texture_handles == NULL) + ctx->texture_handles = g_array_new (FALSE, FALSE, sizeof (CoglHandle)); + + g_array_append_val (ctx->texture_handles, handle); + + return handle; +} + +/* + * _cogl_texture_handle_release: + * @handle: A valid CoglHandle + * + * Frees the given CoglHandle for use with another object. + */ +static void +_cogl_texture_handle_release (CoglHandle handle) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + gint i; + + if ( (i = _cogl_texture_handle_find (handle)) == -1) + return; + + g_array_remove_index_fast (ctx->texture_handles, i); +} + +/* + * _cogl_texture_pointer_from_handle: + * @handle: A valid CoglHandle + * + * Returns a pointer to the texture object referenced by + * given handle. + */ +CoglTexture * +_cogl_texture_pointer_from_handle (CoglHandle handle) +{ + return (CoglTexture*) handle; +} + +CoglHandle +_cogl_texture_handle_from_pointer (CoglTexture *tex) +{ + return (CoglHandle) tex; +} + +gboolean +cogl_is_texture (CoglHandle handle) +{ + if (handle == COGL_INVALID_HANDLE) + return FALSE; + + return _cogl_texture_handle_find (handle) >= 0; +} + +static void +_cogl_texture_bitmap_free (CoglTexture *tex) +{ + if (tex->bitmap.data != NULL && tex->bitmap_owner) + g_free (tex->bitmap.data); + + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; +} + +static void +_cogl_texture_bitmap_swap (CoglTexture *tex, + CoglBitmap *new_bitmap) +{ + if (tex->bitmap.data != NULL && tex->bitmap_owner) + g_free (tex->bitmap.data); + + tex->bitmap = *new_bitmap; + tex->bitmap_owner = TRUE; +} + +static void +_cogl_span_iter_update (CoglSpanIter *iter) +{ + /* Pick current span */ + iter->span = &g_array_index (iter->array, + CoglTexSliceSpan, + iter->index); + + /* Offset next position by span size */ + iter->next_pos = iter->pos + + CLUTTER_INT_TO_FIXED (iter->span->size - iter->span->waste); + + /* Check if span intersects the area to cover */ + if (iter->next_pos <= iter->cover_start || + iter->pos >= iter->cover_end) + { + /* Intersection undefined */ + iter->intersects = FALSE; + return; + } + + iter->intersects = TRUE; + + /* Clip start position to coverage area */ + if (iter->pos < iter->cover_start) + iter->intersect_start = iter->cover_start; + else + iter->intersect_start = iter->pos; + + /* Clip end position to coverage area */ + if (iter->next_pos > iter->cover_end) + iter->intersect_end = iter->cover_end; + else + iter->intersect_end = iter->next_pos; +} + +static void +_cogl_span_iter_begin (CoglSpanIter *iter, + GArray *array, + ClutterFixed origin, + ClutterFixed cover_start, + ClutterFixed cover_end) +{ + /* Copy info */ + iter->index = 0; + iter->array = array; + iter->span = NULL; + iter->origin = origin; + iter->cover_start = cover_start; + iter->cover_end = cover_end; + iter->pos = iter->origin; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +void +_cogl_span_iter_next (CoglSpanIter *iter) +{ + /* Move current position */ + iter->pos = iter->next_pos; + + /* Pick next slice (wrap when last reached) */ + iter->index = (iter->index + 1) % iter->array->len; + + /* Update intersection */ + _cogl_span_iter_update (iter); +} + +static gboolean +_cogl_span_iter_end (CoglSpanIter *iter) +{ + /* End reached when whole area covered */ + return iter->pos >= iter->cover_end; +} + +static gboolean +_cogl_texture_upload_to_gl (CoglTexture *tex) +{ + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + GLuint gl_handle; + gint bpp; + gint x,y; + CoglBitmap slice_bmp; + + /* FIXME: might optimize by not copying to intermediate slice + bitmap when source rowstride = bpp * width and the texture + image is not sliced */ + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Iterate vertical slices */ + for (y = 0; y < tex->slice_y_spans->len; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + /* Iterate horizontal slices */ + for (x = 0; x < tex->slice_x_spans->len; ++x) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + + /* Pick the gl texture object handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y * tex->slice_x_spans->len + x); + + /* Setup temp bitmap for slice subregion */ + slice_bmp.format = tex->bitmap.format; + slice_bmp.width = x_span->size - x_span->waste; + slice_bmp.height = y_span->size - y_span->waste; + slice_bmp.rowstride = bpp * slice_bmp.width; + slice_bmp.data = (guchar*) g_malloc (slice_bmp.rowstride * + slice_bmp.height); + + /* Copy subregion data */ + _cogl_bitmap_copy_subregion (&tex->bitmap, + &slice_bmp, + x_span->start, + y_span->start, + 0, 0, + slice_bmp.width, + slice_bmp.height); + + /* Upload new image data */ + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glPixelStorei (GL_UNPACK_ALIGNMENT, 1) ); + + GE( glTexSubImage2D (tex->gl_target, 0, 0, 0, + slice_bmp.width, + slice_bmp.height, + tex->gl_format, tex->gl_type, + slice_bmp.data) ); + + /* Free temp bitmap */ + g_free (slice_bmp.data); + } + } + + return TRUE; +} + +static gboolean +_cogl_texture_download_from_gl (CoglTexture *tex, + CoglBitmap *target_bmp, + GLuint target_gl_format, + GLuint target_gl_type) +{ + gint bpp; + GLint viewport[4]; + CoglHandle handle; + ClutterColor cback = {0x0, 0x0, 0x0, 0x0}; + ClutterColor cwhite = {0xFF, 0xFF, 0xFF, 0xFF}; + CoglBitmap rect_bmp; + ClutterFixed rx1, ry1; + ClutterFixed rx2, ry2; + ClutterFixed tx1, ty1; + ClutterFixed tx2, ty2; + int bw, bh; + + handle = _cogl_texture_handle_from_pointer (tex); + bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); + + /* Viewport needs to have some size and be inside the window for this */ + GE( glGetIntegerv (GL_VIEWPORT, viewport) ); + + if (viewport[0] < 0 || viewport[1] < 0 || + viewport[2] <= 0 || viewport[3] <= 0) + return FALSE; + + /* Setup orthographic projection into current viewport + (0,0 in bottom-left corner to draw the texture + upside-down so we match the way glReadPixels works) */ + + GE( glMatrixMode (GL_PROJECTION) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + + GE( glOrthox (0, CLUTTER_INT_TO_FIXED (viewport[2]), + 0, CLUTTER_INT_TO_FIXED (viewport[3]), + CLUTTER_INT_TO_FIXED (0), + CLUTTER_INT_TO_FIXED (100)) ); + + GE( glMatrixMode (GL_MODELVIEW) ); + GE( glPushMatrix () ); + GE( glLoadIdentity () ); + + /* Draw to all channels */ + cogl_draw_buffer (COGL_WINDOW_BUFFER | COGL_MASK_BUFFER, 0); + + + /* If whole image fits into the viewport and target buffer + has got no special rowstride, we can do it in one pass */ + if (tex->bitmap.width < viewport[2] - viewport[0] && + tex->bitmap.height < viewport[3] - viewport[1] && + tex->bitmap.rowstride == bpp * tex->bitmap.width) + { + /* Clear buffer with transparent black, draw with white + for direct copy to framebuffer */ + cogl_paint_init (&cback); + cogl_color (&cwhite); + + /* Draw the texture image */ + cogl_texture_rectangle (handle, + 0, 0, + CLUTTER_INT_TO_FIXED (tex->bitmap.width), + CLUTTER_INT_TO_FIXED (tex->bitmap.height), + 0, 0, CFX_ONE, CFX_ONE); + + /* Read into target bitmap */ + GE( glPixelStorei (GL_PACK_ALIGNMENT, 1) ); + GE( glReadPixels (viewport[0], viewport[1], + tex->bitmap.width, + tex->bitmap.height, + GL_RGBA, GL_UNSIGNED_BYTE, + target_bmp->data) ); + } + else + { + ry1 = 0; ry2 = 0; + ty1 = 0; ty2 = 0; + +#define CFIX CLUTTER_INT_TO_FIXED + + /* Walk Y axis until whole bitmap height consumed */ + for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3]) + { + /* Rectangle Y coords */ + ry1 = ry2; + ry2 += (bh < viewport[3]) ? bh : viewport[3]; + + /* Normalized texture Y coords */ + ty1 = ty2; + ty2 = CFX_QDIV (CFIX (ry2), CFIX (tex->bitmap.height)); + + rx1 = 0; rx2 = 0; + tx1 = 0; tx2 = 0; + + /* Walk X axis until whole bitmap width consumed */ + for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2]) + { + /* Rectangle X coords */ + rx1 = rx2; + rx2 += (bw < viewport[2]) ? bw : viewport[2]; + + /* Normalized texture X coords */ + tx1 = tx2; + tx2 = CFX_QDIV (CFIX (rx2), CFIX (tex->bitmap.width)); + + /* Clear buffer with transparent black, draw with white + for direct copy to framebuffer */ + cogl_paint_init (&cback); + cogl_color (&cwhite); + + /* Draw a portion of texture */ + cogl_texture_rectangle (handle, + 0, 0, + CFIX (rx2 - rx1), + CFIX (ry2 - ry1), + tx1, ty1, + tx2, ty2); + + /* Read into a temporary bitmap */ + rect_bmp.format = COGL_PIXEL_FORMAT_RGBA_8888; + rect_bmp.width = rx2 - rx1; + rect_bmp.height = ry2 - ry1; + rect_bmp.rowstride = bpp * rect_bmp.width; + rect_bmp.data = (guchar*) g_malloc (rect_bmp.rowstride * + rect_bmp.height); + + GE( glPixelStorei (GL_PACK_ALIGNMENT, 1) ); + GE( glReadPixels (viewport[0], viewport[1], + rect_bmp.width, + rect_bmp.height, + GL_RGBA, GL_UNSIGNED_BYTE, + rect_bmp.data) ); + + /* Copy to target bitmap */ + _cogl_bitmap_copy_subregion (&rect_bmp, + target_bmp, + 0,0, + rx1,ry1, + rect_bmp.width, + rect_bmp.height); + + /* Free temp bitmap */ + g_free (rect_bmp.data); + } + } + +#undef CFIX + } + + glMatrixMode (GL_PROJECTION); + glPopMatrix (); + glMatrixMode (GL_MODELVIEW); + glPopMatrix (); + + cogl_draw_buffer (COGL_WINDOW_BUFFER, 0); + + return TRUE; +} + +static void +_cogl_texture_set_alignment_for_rowstride (gint rowstride) +{ + gint alignment = 1; + + while ((rowstride & 1) == 0 && alignment < 8) + { + alignment <<= 1; + rowstride >>= 1; + } + + GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) ); +} + +static gboolean +_cogl_texture_upload_subregion_to_gl (CoglTexture *tex, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + gint width, + gint height, + CoglBitmap *source_bmp, + GLuint source_gl_format, + GLuint source_gl_type) +{ + gint bpp; + CoglSpanIter x_iter; + CoglSpanIter y_iter; + GLuint gl_handle; + gint source_x = 0, source_y = 0; + gint inter_w = 0, inter_h = 0; + gint local_x = 0, local_y = 0; + CoglBitmap slice_bmp; + + bpp = _cogl_get_format_bpp (source_bmp->format); + + /* FIXME: might optimize by not copying to intermediate slice + bitmap when source rowstride = bpp * width and the texture + image is not sliced */ + + /* Iterate vertical spans */ + for (source_y = src_y, + _cogl_span_iter_begin (&y_iter, tex->slice_y_spans, + 0, CLUTTER_INT_TO_FIXED (dst_y), + CLUTTER_INT_TO_FIXED (dst_y + height)); + + !_cogl_span_iter_end (&y_iter); + + _cogl_span_iter_next (&y_iter), + source_y += inter_h ) + { + /* Skip non-intersecting ones */ + if (!y_iter.intersects) + { + inter_h = 0; + continue; + } + + /* Iterate horizontal spans */ + for (source_x = src_x, + _cogl_span_iter_begin (&x_iter, tex->slice_x_spans, + 0, CLUTTER_INT_TO_FIXED (dst_x), + CLUTTER_INT_TO_FIXED (dst_x + width)); + + !_cogl_span_iter_end (&x_iter); + + _cogl_span_iter_next (&x_iter), + source_x += inter_w ) + { + /* Skip non-intersecting ones */ + if (!x_iter.intersects) + { + inter_w = 0; + continue; + } + + /* Pick intersection width and height */ + inter_w = CLUTTER_FIXED_TO_INT (x_iter.intersect_end - + x_iter.intersect_start); + inter_h = CLUTTER_FIXED_TO_INT (y_iter.intersect_end - + y_iter.intersect_start); + + /* Localize intersection top-left corner to slice*/ + local_x = CLUTTER_FIXED_TO_INT (x_iter.intersect_start - + x_iter.pos); + local_y = CLUTTER_FIXED_TO_INT (y_iter.intersect_start - + y_iter.pos); + + /* Pick slice GL handle */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + y_iter.index * tex->slice_x_spans->len + + x_iter.index); + + /* Setup temp bitmap for slice subregion */ + slice_bmp.format = tex->bitmap.format; + slice_bmp.width = inter_w; + slice_bmp.height = inter_h; + slice_bmp.rowstride = bpp * slice_bmp.width; + slice_bmp.data = (guchar*) malloc (slice_bmp.rowstride * + slice_bmp.height); + + /* Copy subregion data */ + _cogl_bitmap_copy_subregion (source_bmp, + &slice_bmp, + source_x, + source_y, + 0, 0, + slice_bmp.width, + slice_bmp.height); + + /* Upload new image data */ + GE( glPixelStorei (GL_UNPACK_ALIGNMENT, 1) ); + + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glTexSubImage2D (tex->gl_target, 0, + local_x, local_y, + inter_w, inter_h, + source_gl_format, + source_gl_type, + slice_bmp.data) ); + + /* Free temp bitmap */ + g_free (slice_bmp.data); + } + } + + return TRUE; +} + +static gint +_cogl_pot_slices_for_size (gint size_to_fill, + gint max_span_size, + gint max_waste, + GArray *out_spans) +{ + gint n_spans = 0; + CoglTexSliceSpan span; + + /* Init first slice span */ + span.start = 0; + span.size = max_span_size; + span.waste = 0; + + /* Fix invalid max_waste */ + if (max_waste < 0) max_waste = 0; + + while (TRUE) + { + /* Is the whole area covered? */ + if (size_to_fill > span.size) + { + /* Not yet - add a span of this size */ + if (out_spans) g_array_append_val (out_spans, span); + span.start += span.size; + size_to_fill -= span.size; + n_spans++; + } + else if (span.size - size_to_fill <= max_waste) + { + /* Yes and waste is small enough */ + span.waste = span.size - size_to_fill; + if (out_spans) g_array_append_val (out_spans, span); + return ++n_spans; + } + else + { + /* Yes but waste is too large */ + while (span.size - size_to_fill > max_waste) + { + span.size /= 2; + g_assert (span.size > 0); + } + } + } + + /* Can't get here */ + return 0; +} + +static gboolean +_cogl_texture_size_supported (GLenum gl_target, + GLenum gl_format, + GLenum gl_type, + int width, + int height) +{ + return TRUE; +} + +static gboolean +_cogl_texture_slices_create (CoglTexture *tex) +{ + gint bpp; + gint max_width; + gint max_height; + GLuint *gl_handles; + gint n_x_slices; + gint n_y_slices; + gint n_slices; + gint x, y; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + + gint (*slices_for_size) (gint, gint, gint, GArray*); + + bpp = _cogl_get_format_bpp (tex->bitmap.format); + + /* Initialize size of largest slice according to supported features */ + max_width = cogl_util_next_p2 (tex->bitmap.width); + max_height = cogl_util_next_p2 (tex->bitmap.height); + tex->gl_target = GL_TEXTURE_2D; + slices_for_size = _cogl_pot_slices_for_size; + + /* Negative number means no slicing forced by the user */ + if (tex->max_waste <= -1) + { + CoglTexSliceSpan span; + + /* Check if size supported else bail out */ + if (!_cogl_texture_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + return FALSE; + } + + n_x_slices = 1; + n_y_slices = 1; + + /* Init span arrays */ + tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + 1); + + /* Add a single span for width and height */ + span.start = 0; + span.size = max_width; + span.waste = max_width - tex->bitmap.width; + g_array_append_val (tex->slice_x_spans, span); + + span.size = max_height; + span.waste = max_height - tex->bitmap.height; + g_array_append_val (tex->slice_y_spans, span); + } + else + { + /* Decrease the size of largest slice until supported by GL */ + while (!_cogl_texture_size_supported (tex->gl_target, + tex->gl_format, + tex->gl_type, + max_width, + max_height)) + { + /* Alternate between width and height */ + if (max_width > max_height) + max_width /= 2; + else + max_height /= 2; + + if (max_width == 0 || max_height == 0) + return FALSE; + } + + /* Determine the slices required to cover the bitmap area */ + n_x_slices = slices_for_size (tex->bitmap.width, + max_width, tex->max_waste, + NULL); + + n_y_slices = slices_for_size (tex->bitmap.height, + max_height, tex->max_waste, + NULL); + + /* Init span arrays with reserved size */ + tex->slice_x_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_x_slices); + + tex->slice_y_spans = g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), + n_y_slices); + + /* Fill span arrays with info */ + slices_for_size (tex->bitmap.width, + max_width, tex->max_waste, + tex->slice_x_spans); + + slices_for_size (tex->bitmap.height, + max_height, tex->max_waste, + tex->slice_y_spans); + } + + /* Init and resize GL handle array */ + n_slices = n_x_slices * n_y_slices; + + tex->slice_gl_handles = g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), + n_slices); + + g_array_set_size (tex->slice_gl_handles, n_slices); + + /* Generate a "working set" of GL texture objects + * (some implementations might supported faster + * re-binding between textures inside a set) */ + gl_handles = (GLuint*) tex->slice_gl_handles->data; + + GE( glGenTextures (n_slices, gl_handles) ); + + + /* Init each GL texture object */ + for (y = 0; y < n_y_slices; ++y) + { + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, y); + + for (x = 0; x < n_x_slices; ++x) + { + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, x); + +#if COGL_DEBUG + printf ("CREATE SLICE (%d,%d)\n", x,y); + printf ("size: (%d x %d)\n", + x_span->size - x_span->waste, + y_span->size - y_span->waste); +#endif + /* Setup texture parameters */ + GE( glBindTexture (tex->gl_target, gl_handles[y * n_x_slices + x]) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); + + /* Pass NULL data to init size and internal format */ + GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat, + x_span->size, y_span->size, 0, + tex->gl_format, tex->gl_type, 0) ); + } + } + + return TRUE; +} + +static void +_cogl_texture_slices_free (CoglTexture *tex) +{ + if (tex->slice_x_spans != NULL) + g_array_free (tex->slice_x_spans, TRUE); + + if (tex->slice_y_spans != NULL) + g_array_free (tex->slice_y_spans, TRUE); + + if (tex->slice_gl_handles != NULL) + { + if (tex->is_foreign == FALSE) + { + GE( glDeleteTextures (tex->slice_gl_handles->len, + (GLuint*) tex->slice_gl_handles->data) ); + } + + g_array_free (tex->slice_gl_handles, TRUE); + } +} + +static gboolean +_cogl_pixel_format_from_gl_internal (GLenum gl_int_format, + CoglPixelFormat *out_format) +{ + /* It doesn't really matter we convert to exact same + format (some have no cogl match anyway) since format + is re-matched against cogl when getting or setting + texture image data. + */ + + switch (gl_int_format) + { + case GL_ALPHA: + + *out_format = COGL_PIXEL_FORMAT_A_8; + return TRUE; + + case GL_LUMINANCE: + + *out_format = COGL_PIXEL_FORMAT_G_8; + return TRUE; + + case GL_RGB: + + *out_format = COGL_PIXEL_FORMAT_RGB_888; + return TRUE; + + case GL_RGBA: + + *out_format = COGL_PIXEL_FORMAT_RGBA_8888; + return TRUE; + } + + return FALSE; +} + +static CoglPixelFormat +_cogl_pixel_format_to_gl (CoglPixelFormat format, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype) +{ + CoglPixelFormat required_format = format; + GLenum glintformat = 0; + GLenum glformat = 0; + GLenum gltype = 0; + + /* No premultiplied formats accepted by GL + * (FIXME: latest hardware?) */ + + if (format & COGL_PREMULT_BIT) + format = (format & COGL_UNPREMULT_MASK); + + /* Find GL equivalents */ + switch (format) + { + case COGL_PIXEL_FORMAT_A_8: + glintformat = GL_ALPHA; + glformat = GL_ALPHA; + gltype = GL_UNSIGNED_BYTE; + break; + case COGL_PIXEL_FORMAT_G_8: + glintformat = GL_LUMINANCE; + glformat = GL_LUMINANCE; + gltype = GL_UNSIGNED_BYTE; + break; + + /* Just one 24-bit ordering supported */ + case COGL_PIXEL_FORMAT_RGB_888: + case COGL_PIXEL_FORMAT_BGR_888: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_BYTE; + required_format = COGL_PIXEL_FORMAT_RGB_888; + break; + + /* Just one 32-bit ordering supported */ + case COGL_PIXEL_FORMAT_RGBA_8888: + case COGL_PIXEL_FORMAT_BGRA_8888: + case COGL_PIXEL_FORMAT_ARGB_8888: + case COGL_PIXEL_FORMAT_ABGR_8888: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_BYTE; + required_format = COGL_PIXEL_FORMAT_RGBA_8888; + break; + + /* The following three types of channel ordering + * are always defined using system word byte + * ordering (even according to GLES spec) */ + case COGL_PIXEL_FORMAT_RGB_565: + glintformat = GL_RGB; + glformat = GL_RGB; + gltype = GL_UNSIGNED_SHORT_5_6_5; + break; + case COGL_PIXEL_FORMAT_RGBA_4444: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case COGL_PIXEL_FORMAT_RGBA_5551: + glintformat = GL_RGBA; + glformat = GL_RGBA; + gltype = GL_UNSIGNED_SHORT_5_5_5_1; + break; + + /* FIXME: check extensions for YUV support */ + default: + break; + } + + if (out_glintformat != NULL) + *out_glintformat = glintformat; + if (out_glformat != NULL) + *out_glformat = glformat; + if (out_gltype != NULL) + *out_gltype = gltype; + + return required_format; +} + +static gboolean +_cogl_texture_bitmap_prepare (CoglTexture *tex, + CoglPixelFormat internal_format) +{ + CoglBitmap new_bitmap; + CoglPixelFormat new_data_format; + gboolean success; + + /* Was there any internal conversion requested? */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = tex->bitmap.format; + + /* Find closest format accepted by GL */ + new_data_format = _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + /* Convert to internal format */ + if (new_data_format != tex->bitmap.format) + { + success = _cogl_bitmap_convert_and_premult (&tex->bitmap, + &new_bitmap, + new_data_format); + + if (!success) + return FALSE; + + /* Update texture with new data */ + _cogl_texture_bitmap_swap (tex, &new_bitmap); + } + + return TRUE; +} + +static void +_cogl_texture_free (CoglTexture *tex) +{ + /* Frees texture resources but its handle is not + released! Do that separately before this! */ + _cogl_texture_bitmap_free (tex); + _cogl_texture_slices_free (tex); + g_free (tex); +} + +CoglHandle +cogl_texture_new_with_size (guint width, + guint height, + gint max_waste, + CoglPixelFormat internal_format) +{ + CoglTexture *tex; + gint bpp; + gint rowstride; + + /* Since no data, we need some internal format */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + /* Rowstride from width */ + bpp = _cogl_get_format_bpp (internal_format); + rowstride = width * bpp; + + /* Init texture with empty bitmap */ + tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); + + tex->ref_count = 1; + + tex->is_foreign = FALSE; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.format = internal_format; + tex->bitmap.rowstride = rowstride; + tex->bitmap.data = NULL; + tex->bitmap_owner = FALSE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* Find closest GL format match */ + tex->bitmap.format = + _cogl_pixel_format_to_gl (internal_format, + &tex->gl_intformat, + &tex->gl_format, + &tex->gl_type); + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_data (guint width, + guint height, + gint max_waste, + CoglPixelFormat format, + CoglPixelFormat internal_format, + guint rowstride, + const guchar *data) +{ + CoglTexture *tex; + gint bpp; + + if (format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + if (data == NULL) + return COGL_INVALID_HANDLE; + + /* Rowstride from width if not given */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = width * bpp; + + /* Create new texture and fill with given data */ + tex = (CoglTexture*) g_malloc (sizeof (CoglTexture)); + + tex->ref_count = 1; + + tex->is_foreign = FALSE; + + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.data = (guchar*)data; + tex->bitmap.format = format; + tex->bitmap.rowstride = rowstride; + tex->bitmap_owner = FALSE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle (this + * is to keep the behavior equal to _new_from_file; + * see below) */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_to_gl (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_handle_new (tex);; +} + +CoglHandle +cogl_texture_new_from_file (const gchar *filename, + gint max_waste, + CoglPixelFormat internal_format, + GError **error) +{ + CoglBitmap bmp; + CoglTexture *tex; + + g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); + + /* Try loading with imaging backend */ + if (!_cogl_bitmap_from_file (&bmp, filename, error)) + { + /* Try fallback */ + if (!_cogl_bitmap_fallback_from_file (&bmp, filename)) + return COGL_INVALID_HANDLE; + else if (error && *error) + { + g_error_free (*error); + *error = NULL; + } + } + + /* Create new texture and fill with loaded data */ + tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); + + tex->ref_count = 1; + + tex->is_foreign = FALSE; + + tex->bitmap = bmp; + tex->bitmap_owner = TRUE; + + tex->slice_x_spans = NULL; + tex->slice_y_spans = NULL; + tex->slice_gl_handles = NULL; + + tex->max_waste = max_waste; + tex->min_filter = CGL_NEAREST; + tex->mag_filter = CGL_NEAREST; + + /* FIXME: If upload fails we should set some kind of + * error flag but still return texture handle if the + * user decides to destroy another texture and upload + * this one instead (reloading from file is not needed + * in that case). As a rule then, everytime a valid + * CoglHandle is returned, it should also be destroyed + * with cogl_texture_unref at some point! */ + + if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_slices_create (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_upload_to_gl (tex)) + { + _cogl_texture_free (tex); + return COGL_INVALID_HANDLE; + } + + _cogl_texture_bitmap_free (tex); + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_new_from_foreign (GLuint gl_handle, + GLenum gl_target, + GLuint width, + GLuint height, + GLuint x_pot_waste, + GLuint y_pot_waste, + CoglPixelFormat format) +{ + GLenum gl_error = 0; + GLboolean gl_istexture; + GLint gl_min_filter; + GLint gl_mag_filter; + guint bpp; + CoglTexture *tex; + CoglTexSliceSpan x_span; + CoglTexSliceSpan y_span; + + /* Allow 2-dimensional textures only */ + if (gl_target != GL_TEXTURE_2D) + return COGL_INVALID_HANDLE; + + /* Make sure it is a valid GL texture object */ + gl_istexture = GE( glIsTexture (gl_handle) ); + if (gl_istexture == GL_FALSE) + return COGL_INVALID_HANDLE; + + /* Make sure binding succeeds */ + gl_error = glGetError (); + glBindTexture (gl_target, gl_handle); + if (glGetError () != GL_NO_ERROR) + return COGL_INVALID_HANDLE; + + /* Obtain texture parameters + (only level 0 we are interested in) */ + + /* These are not queriable in GLES :( + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_COMPRESSED, + &gl_compressed) ); + + GE( glGetTexLevelParameteriv (gl_target, 0, + GL_TEXTURE_INTERNAL_FORMAT, + &gl_int_format) ); + */ + GE( glGetTexParameteriv (gl_target, + GL_TEXTURE_MIN_FILTER, + &gl_min_filter)); + + GE( glGetTexParameteriv (gl_target, + GL_TEXTURE_MAG_FILTER, + &gl_mag_filter)); + + /* Validate width and height */ + if (width <= 0 || height <= 0) + return COGL_INVALID_HANDLE; + + /* Validate pot waste */ + if (x_pot_waste < 0 || x_pot_waste >= width || + y_pot_waste < 0 || y_pot_waste >= height) + return COGL_INVALID_HANDLE; + + /* Create new texture */ + tex = (CoglTexture*) g_malloc ( sizeof (CoglTexture)); + + tex->ref_count = 1; +#if COGL_DEBUG + printf ("COGL TEX new %p %i\n", tex, tex->ref_count); +#endif + + /* Setup bitmap info */ + tex->is_foreign = TRUE; + + bpp = _cogl_get_format_bpp (format); + tex->bitmap.format = format; + tex->bitmap.width = width; + tex->bitmap.height = height; + tex->bitmap.rowstride = tex->bitmap.width * bpp; + tex->bitmap_owner = FALSE; + + tex->gl_target = gl_target; + tex->gl_intformat = 0; + tex->gl_format = 0; + tex->gl_type = 0; + + tex->min_filter = gl_min_filter; + tex->mag_filter = gl_mag_filter; + tex->max_waste = 0; + + /* Create slice arrays */ + tex->slice_x_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex->slice_y_spans = + g_array_sized_new (FALSE, FALSE, + sizeof (CoglTexSliceSpan), 1); + + tex->slice_gl_handles = + g_array_sized_new (FALSE, FALSE, + sizeof (GLuint), 1); + + /* Store info for a single slice */ + x_span.start = 0; + x_span.size = width + x_pot_waste; + x_span.waste = x_pot_waste; + g_array_append_val (tex->slice_x_spans, x_span); + + y_span.start = 0; + y_span.size = height + x_pot_waste; + y_span.waste = y_pot_waste; + g_array_append_val (tex->slice_y_spans, y_span); + + g_array_append_val (tex->slice_gl_handles, gl_handle); + + /* Replace mipmap min filter modes with single level ones */ + if (gl_min_filter != GL_NEAREST && gl_min_filter != GL_LINEAR) + { + if (gl_min_filter == GL_NEAREST_MIPMAP_NEAREST) + { + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST) ); + tex->min_filter = CGL_NEAREST; + } + else + { + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) ); + tex->min_filter = CGL_LINEAR; + } + } + + /* Force appropriate wrap parameter */ + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) ); + + return _cogl_texture_handle_new (tex); +} + +CoglHandle +cogl_texture_ref (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return COGL_INVALID_HANDLE; + + tex = _cogl_texture_pointer_from_handle (handle); + + tex->ref_count++; + + return handle; +} + +void +cogl_texture_unref (CoglHandle handle) +{ + /* Check if valid texture handle */ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (--tex->ref_count < 1) + { + /* Free texture handle and resources */ + _cogl_texture_handle_release (tex); + _cogl_texture_free (tex); + } +} + +void +cogl_texture_get_properties (CoglHandle handle, + guint *out_width, + guint *out_height, + CoglPixelFormat *out_format, + guint *out_rowstride, + guint *out_max_waste) +{ + CoglTexture *tex; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Output requested properties */ + if (out_width != NULL) + *out_width = tex->bitmap.width; + + if (out_height != NULL) + *out_height = tex->bitmap.height; + + if (out_format != NULL) + *out_format = tex->bitmap.format; + + if (out_rowstride != NULL) + *out_rowstride = tex->bitmap.rowstride; + + if (out_max_waste != NULL) + *out_max_waste = tex->max_waste; +} + +guint +cogl_texture_get_width (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.width; +} + +guint +cogl_texture_get_height (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.height; +} + +CoglPixelFormat +cogl_texture_get_format (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return COGL_PIXEL_FORMAT_ANY; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.format; +} + +guint +cogl_texture_get_rowstride (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->bitmap.rowstride; +} + +gint +cogl_texture_get_max_waste (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->max_waste; +} + +gboolean +cogl_texture_is_sliced (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (tex->slice_gl_handles == NULL) + return FALSE; + + if (tex->slice_gl_handles->len <= 1) + return FALSE; + + return TRUE; +} + +gboolean +cogl_texture_get_gl_texture (CoglHandle handle, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + if (tex->slice_gl_handles == NULL) + return FALSE; + + if (tex->slice_gl_handles->len < 1) + return FALSE; + + if (out_gl_handle != NULL) + *out_gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + + if (out_gl_target != NULL) + *out_gl_target = tex->gl_target; + + return TRUE; +} + +COGLenum +cogl_texture_get_min_filter (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->min_filter; +} + +COGLenum +cogl_texture_get_mag_filter (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + return tex->mag_filter; +} + +void +cogl_texture_set_filters (CoglHandle handle, + COGLenum min_filter, + COGLenum mag_filter) +{ + CoglTexture *tex; + GLuint gl_handle; + int i; + + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Store new values */ + tex->min_filter = min_filter; + tex->mag_filter = mag_filter; + + /* Make sure slices were created */ + if (tex->slice_gl_handles == NULL) + return; + + /* Apply new filters to every slice */ + for (i=0; islice_gl_handles->len; ++i) + { + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, i); + GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, tex->mag_filter) ); + GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, tex->min_filter) ); + } +} + +gboolean +cogl_texture_set_region (CoglHandle handle, + gint src_x, + gint src_y, + gint dst_x, + gint dst_y, + guint dst_width, + guint dst_height, + gint width, + gint height, + CoglPixelFormat format, + guint rowstride, + const guchar *data) +{ + CoglTexture *tex; + gint bpp; + CoglBitmap source_bmp; + CoglBitmap temp_bmp; + gboolean source_bmp_owner = FALSE; + CoglPixelFormat closest_format; + GLenum closest_gl_format; + GLenum closest_gl_type; + gboolean success; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return FALSE; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Check for valid format */ + if (format == COGL_PIXEL_FORMAT_ANY) + return FALSE; + + /* Init source bitmap */ + source_bmp.width = width; + source_bmp.height = height; + source_bmp.format = format; + source_bmp.data = (guchar*)data; + + /* Rowstride from width if none specified */ + bpp = _cogl_get_format_bpp (format); + source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; + + /* Find closest format to internal that's supported by GL */ + closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format, + NULL, /* don't need */ + &closest_gl_format, + &closest_gl_type); + + /* If no direct match, convert */ + if (closest_format != format) + { + /* Convert to required format */ + success = _cogl_bitmap_convert_and_premult (&source_bmp, + &temp_bmp, + closest_format); + + /* Swap bitmaps if succeeded */ + if (!success) return FALSE; + source_bmp = temp_bmp; + source_bmp_owner = TRUE; + } + + /* Send data to GL */ + _cogl_texture_upload_subregion_to_gl (tex, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + &source_bmp, + closest_gl_format, + closest_gl_type); + + /* Free data if owner */ + if (source_bmp_owner) + g_free (source_bmp.data); + + return TRUE; +} + +gint +cogl_texture_get_data (CoglHandle handle, + CoglPixelFormat format, + guint rowstride, + guchar *data) +{ + CoglTexture *tex; + gint bpp; + gint byte_size; + CoglPixelFormat closest_format; + gint closest_bpp; + GLenum closest_gl_format; + GLenum closest_gl_type; + CoglBitmap target_bmp; + CoglBitmap new_bmp; + gboolean success; + guchar *src; + guchar *dst; + gint y; + + /* Check if valid texture handle */ + if (!cogl_is_texture (handle)) + return 0; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = tex->bitmap.format; + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = tex->bitmap.width * bpp; + + /* Return byte size if only that requested */ + byte_size = tex->bitmap.height * rowstride; + if (data == NULL) return byte_size; + + /* Find closest format that's supported by GL + (Can't use _cogl_pixel_format_to_gl since available formats + when reading pixels on GLES are severely limited) */ + closest_format = COGL_PIXEL_FORMAT_RGBA_8888; + closest_gl_format = GL_RGBA; + closest_gl_type = GL_UNSIGNED_BYTE; + closest_bpp = _cogl_get_format_bpp (closest_format); + + /* Is the requested format supported? */ + if (closest_format == format) + { + /* Target user data directly */ + target_bmp = tex->bitmap; + target_bmp.format = format; + target_bmp.rowstride = rowstride; + target_bmp.data = data; + } + else + { + /* Target intermediate buffer */ + target_bmp = tex->bitmap; + target_bmp.format = closest_format; + target_bmp.rowstride = target_bmp.width * closest_bpp; + target_bmp.data = (guchar*) g_malloc (target_bmp.height + * target_bmp.rowstride); + } + + /* Retrieve data from slices */ + _cogl_texture_download_from_gl (tex, &target_bmp, + closest_gl_format, + closest_gl_type); + + /* Was intermediate used? */ + if (closest_format != format) + { + /* Convert to requested format */ + success = _cogl_bitmap_convert_and_premult (&target_bmp, + &new_bmp, + format); + + /* Free intermediate data and return if failed */ + g_free (target_bmp.data); + if (!success) return 0; + + /* Copy to user buffer */ + for (y = 0; y < new_bmp.height; ++y) + { + src = new_bmp.data + y * new_bmp.rowstride; + dst = data + y * rowstride; + memcpy (dst, src, new_bmp.width); + } + + /* Free converted data */ + g_free (new_bmp.data); + } + + return byte_size; +} + +static void +_cogl_texture_quad_sw (CoglTexture *tex, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + CoglSpanIter iter_x , iter_y; + ClutterFixed tw , th; + ClutterFixed tqx , tqy; + ClutterFixed first_tx , first_ty; + ClutterFixed first_qx , first_qy; + ClutterFixed slice_tx1 , slice_ty1; + ClutterFixed slice_tx2 , slice_ty2; + ClutterFixed slice_qx1 , slice_qy1; + ClutterFixed slice_qx2 , slice_qy2; + GLfixed tex_coords[8]; + GLfixed quad_coords[8]; + GLuint gl_handle; + +#if COGL_DEBUG + printf("=== Drawing Tex Quad (Software Tiling Mode) ===\n"); +#endif + + /* Prepare GL state */ + gulong enable_flags = (COGL_ENABLE_TEXTURE_2D + | COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + { + enable_flags |= COGL_ENABLE_BLEND; + } + + cogl_enable (enable_flags); + + GE( glTexCoordPointer (2, GL_FIXED, 0, tex_coords) ); + GE( glVertexPointer (2, GL_FIXED, 0, quad_coords) ); + + /* Scale ratio from texture to quad widths */ + tw = CLUTTER_INT_TO_FIXED (tex->bitmap.width); + th = CLUTTER_INT_TO_FIXED (tex->bitmap.height); + + tqx = CFX_QDIV (x2-x1, CFX_QMUL (tw, (tx2 - tx1))); + tqy = CFX_QDIV (y2-y1, CFX_QMUL (th, (ty2 - ty1))); + + /* Integral texture coordinate for first tile */ + first_tx = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (tx1)); + first_ty = CLUTTER_INT_TO_FIXED (CLUTTER_FIXED_FLOOR (ty1)); + + /* Denormalize texture coordinates */ + first_tx = CFX_QMUL (first_tx, tw); + first_ty = CFX_QMUL (first_ty, th); + tx1 = CFX_QMUL (tx1, tw); + ty1 = CFX_QMUL (ty1, th); + tx2 = CFX_QMUL (tx2, tw); + ty2 = CFX_QMUL (ty2, th); + + /* Quad coordinate of the first tile */ + first_qx = x1 - CFX_QMUL (tx1 - first_tx, tqx); + first_qy = y1 - CFX_QMUL (ty1 - first_ty, tqy); + + + /* Iterate until whole quad height covered */ + for (_cogl_span_iter_begin (&iter_y, tex->slice_y_spans, + first_ty, ty1, ty2) ; + !_cogl_span_iter_end (&iter_y) ; + _cogl_span_iter_next (&iter_y) ) + { + /* Discard slices out of quad early */ + if (!iter_y.intersects) continue; + + /* Span-quad intersection in quad coordinates */ + slice_qy1 = first_qy + + CFX_QMUL (iter_y.intersect_start - first_ty, tqy); + + slice_qy2 = first_qy + + CFX_QMUL (iter_y.intersect_end - first_ty, tqy); + + /* Localize slice texture coordinates */ + slice_ty1 = iter_y.intersect_start - iter_y.pos; + slice_ty2 = iter_y.intersect_end - iter_y.pos; + + /* Normalize texture coordinates to current slice */ + slice_ty1 /= iter_y.span->size; + slice_ty2 /= iter_y.span->size; + + + /* Iterate until whole quad width covered */ + for (_cogl_span_iter_begin (&iter_x, tex->slice_x_spans, + first_tx, tx1, tx2) ; + !_cogl_span_iter_end (&iter_x) ; + _cogl_span_iter_next (&iter_x) ) + { + /* Discard slices out of quad early */ + if (!iter_x.intersects) continue; + + /* Span-quad intersection in quad coordinates */ + slice_qx1 = first_qx + + CFX_QMUL (iter_x.intersect_start - first_tx, tqx); + + slice_qx2 = first_qx + + CFX_QMUL (iter_x.intersect_end - first_tx, tqx); + + /* Localize slice texture coordinates */ + slice_tx1 = iter_x.intersect_start - iter_x.pos; + slice_tx2 = iter_x.intersect_end - iter_x.pos; + + /* Normalize texture coordinates to current slice */ + slice_tx1 /= iter_x.span->size; + slice_tx2 /= iter_x.span->size; + +#if COGL_DEBUG + printf("~~~~~ slice (%d,%d)\n", iter_x.index, iter_y.index); + printf("qx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx1)); + printf("qy1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy1)); + printf("qx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qx2)); + printf("qy2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_qy2)); + printf("tx1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx1)); + printf("ty1: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty1)); + printf("tx2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_tx2)); + printf("ty2: %f\n", CLUTTER_FIXED_TO_FLOAT (slice_ty2)); +#endif + + /* Pick and bind opengl texture object */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, + iter_y.index * iter_x.array->len + + iter_x.index); + + GE( glBindTexture (tex->gl_target, gl_handle) ); + + /* Draw textured quad */ + tex_coords[0] = slice_tx1; tex_coords[1] = slice_ty1; + tex_coords[2] = slice_tx2; tex_coords[3] = slice_ty1; + tex_coords[4] = slice_tx1; tex_coords[5] = slice_ty2; + tex_coords[6] = slice_tx2; tex_coords[7] = slice_ty2; + + quad_coords[0] = slice_qx1; quad_coords[1] = slice_qy1; + quad_coords[2] = slice_qx2; quad_coords[3] = slice_qy1; + quad_coords[4] = slice_qx1; quad_coords[5] = slice_qy2; + quad_coords[6] = slice_qx2; quad_coords[7] = slice_qy2; + + GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); + } + } +} + +static void +_cogl_texture_quad_hw (CoglTexture *tex, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + GLfixed tex_coords[8]; + GLfixed quad_coords[8]; + GLuint gl_handle; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + +#if COGL_DEBUG + printf("=== Drawing Tex Quad (Hardware Tiling Mode) ===\n"); +#endif + + /* Prepare GL state */ + gulong enable_flags = (COGL_ENABLE_TEXTURE_2D + | COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); + + if (ctx->color_alpha < 255 + || tex->bitmap.format & COGL_A_BIT) + { + enable_flags |= COGL_ENABLE_BLEND; + } + + cogl_enable (enable_flags); + + GE( glTexCoordPointer (2, GL_FIXED, 0, tex_coords) ); + GE( glVertexPointer (2, GL_FIXED, 0, quad_coords) ); + + /* Pick and bind opengl texture object */ + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + GE( glBindTexture (tex->gl_target, gl_handle) ); + + /* Don't include the waste in the texture coordinates */ + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + tx1 = tx1 * (x_span->size - x_span->waste) / x_span->size; + tx2 = tx2 * (x_span->size - x_span->waste) / x_span->size; + ty1 = ty1 * (y_span->size - y_span->waste) / y_span->size; + ty2 = ty2 * (y_span->size - y_span->waste) / y_span->size; + + /* Draw textured quad */ + tex_coords[0] = tx1; tex_coords[1] = ty1; + tex_coords[2] = tx2; tex_coords[3] = ty1; + tex_coords[4] = tx1; tex_coords[5] = ty2; + tex_coords[6] = tx2; tex_coords[7] = ty2; + + quad_coords[0] = x1; quad_coords[1] = y1; + quad_coords[2] = x2; quad_coords[3] = y1; + quad_coords[4] = x1; quad_coords[5] = y2; + quad_coords[6] = x2; quad_coords[7] = y2; + + GE (glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); +} + +void +cogl_texture_rectangle (CoglHandle handle, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + CoglTexture *tex; + ClutterFixed tempx; + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* Make sure we got stuff to draw */ + if (tex->slice_gl_handles == NULL) + return; + + if (tex->slice_gl_handles->len == 0) + return; + + if (tx1 == tx2 || ty1 == ty2) + return; + + /* Fix quad coord ordering */ + if (x1 > x2) + { + tempx = x1; + x1 = x2; + x2 = tempx; + } + + if (y1 > y2) + { + tempx = y1; + y1 = y2; + y2 = tempx; + } + + /* Fix texture coord ordering */ + if (tx1 > tx2) + { + tempx = tx1; + tx1 = tx2; + tx2 = tempx; + } + + if (ty1 > ty2) + { + tempx = ty1; + ty1 = ty2; + ty2 = tempx; + } + + /* Tile textured quads */ + if (tex->slice_gl_handles->len == 1 + && tx1 >= -CFX_ONE && tx2 <= CFX_ONE + && ty1 >= -CFX_ONE && ty2 <= CFX_ONE) + { + _cogl_texture_quad_hw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } + else + { + _cogl_texture_quad_sw (tex, x1,y1, x2,y2, tx1,ty1, tx2,ty2); + } +} + +void +cogl_texture_polygon (CoglHandle handle, + guint n_vertices, + CoglTextureVertex *vertices, + gboolean use_color) +{ + CoglTexture *tex; + GLuint gl_handle; + gulong enable_flags; + int i; + CoglTextureGLVertex *p; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Check if valid texture */ + if (!cogl_is_texture (handle)) + return; + + tex = _cogl_texture_pointer_from_handle (handle); + + /* GL ES has no GL_CLAMP_TO_BORDER wrap mode so the method used to + render sliced textures in the GL backend will not work. Therefore + cogl_texture_polygon is only supported if the texture is not + sliced */ + if (tex->slice_gl_handles->len != 1) + { + static gboolean shown_warning = FALSE; + + if (!shown_warning) + { + g_warning ("cogl_texture_polygon does not work for " + "sliced textures on GL ES"); + shown_warning = TRUE; + } + return; + } + + /* Make sure there is enough space in the global texture vertex + array. This is used so we can render the polygon with a single + call to OpenGL but still support any number of vertices */ + if (ctx->texture_vertices_size < n_vertices) + { + guint nsize = ctx->texture_vertices_size; + + if (nsize == 0) + nsize = 1; + do + nsize *= 2; + while (nsize < n_vertices); + + ctx->texture_vertices_size = nsize; + + if (ctx->texture_vertices) + ctx->texture_vertices = g_realloc (ctx->texture_vertices, + nsize + * sizeof (CoglTextureGLVertex)); + else + ctx->texture_vertices = g_malloc (nsize + * sizeof (CoglTextureGLVertex)); + } + + /* Prepare GL state */ + enable_flags = (COGL_ENABLE_TEXTURE_2D + | COGL_ENABLE_VERTEX_ARRAY + | COGL_ENABLE_TEXCOORD_ARRAY); + + if ((tex->bitmap.format & COGL_A_BIT)) + enable_flags |= COGL_ENABLE_BLEND; + else if (use_color) + { + for (i = 0; i < n_vertices; i++) + if (vertices[i].color.alpha < 255) + { + enable_flags |= COGL_ENABLE_BLEND; + break; + } + } + else if (ctx->color_alpha < 255) + enable_flags |= COGL_ENABLE_BLEND; + + if (use_color) + { + enable_flags |= COGL_ENABLE_COLOR_ARRAY; + GE( glColorPointer (4, GL_FIXED, sizeof (CoglTextureGLVertex), + ctx->texture_vertices[0].c) ); + } + + GE( glVertexPointer (3, GL_FIXED, sizeof (CoglTextureGLVertex), + ctx->texture_vertices[0].v) ); + GE( glTexCoordPointer (2, GL_FIXED, sizeof (CoglTextureGLVertex), + ctx->texture_vertices[0].t) ); + + cogl_enable (enable_flags); + + gl_handle = g_array_index (tex->slice_gl_handles, GLuint, 0); + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + + /* Convert the vertices into an array of GLfixeds ready to pass to + OpenGL */ + for (i = 0, p = ctx->texture_vertices; i < n_vertices; i++, p++) + { + p->v[0] = vertices[i].x; + p->v[1] = vertices[i].y; + p->v[2] = vertices[i].z; + p->t[0] = vertices[i].tx * (x_span->size - x_span->waste) / x_span->size; + p->t[1] = vertices[i].ty * (y_span->size - y_span->waste) / y_span->size; + p->c[0] = (vertices[i].color.red << 16) / 0xff; + p->c[1] = (vertices[i].color.green << 16) / 0xff; + p->c[2] = (vertices[i].color.blue << 16) / 0xff; + p->c[3] = (vertices[i].color.alpha << 16) / 0xff; + } + + GE( glBindTexture (tex->gl_target, gl_handle) ); + + GE( glDrawArrays (GL_TRIANGLE_FAN, 0, n_vertices) ); + + /* Set the last color so that the cache of the alpha value will work + properly */ + if (use_color && n_vertices > 0) + cogl_color (&vertices[n_vertices - 1].color); +} diff --git a/clutter/cogl/gles/cogl-texture.h b/clutter/cogl/gles/cogl-texture.h new file mode 100644 index 000000000..e2401be07 --- /dev/null +++ b/clutter/cogl/gles/cogl-texture.h @@ -0,0 +1,63 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_TEXTURE_H +#define __COGL_TEXTURE_H + +#include "cogl-bitmap.h" + +typedef struct _CoglTexture CoglTexture; +typedef struct _CoglTexSliceSpan CoglTexSliceSpan; +typedef struct _CoglSpanIter CoglSpanIter; + +struct _CoglTexSliceSpan +{ + gint start; + gint size; + gint waste; +}; + +struct _CoglTexture +{ + guint ref_count; + CoglBitmap bitmap; + gboolean bitmap_owner; + GLenum gl_target; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + GArray *slice_x_spans; + GArray *slice_y_spans; + GArray *slice_gl_handles; + gint max_waste; + COGLenum min_filter; + COGLenum mag_filter; + gboolean is_foreign; +}; + +CoglTexture* +_cogl_texture_pointer_from_handle (CoglHandle handle); + +#endif /* __COGL_TEXTURE_H */ diff --git a/clutter/cogl/gles/cogl-util.c b/clutter/cogl/gles/cogl-util.c new file mode 100644 index 000000000..b024e763e --- /dev/null +++ b/clutter/cogl/gles/cogl-util.c @@ -0,0 +1,51 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" + +/** + * cogl_util_next_p2: + * @a: Value to get the next power + * + * Calculates the next power greater than @a. + * + * Return value: The next power after @a. + */ +int +cogl_util_next_p2 (int a) +{ + int rval=1; + + while(rval < a) + rval <<= 1; + + return rval; +} diff --git a/clutter/cogl/gles/cogl-util.h b/clutter/cogl/gles/cogl-util.h new file mode 100644 index 000000000..759da8cc7 --- /dev/null +++ b/clutter/cogl/gles/cogl-util.h @@ -0,0 +1,32 @@ +/* + * Clutter COGL + * + * A basic GL/GLES Abstraction/Utility Layer + * + * Authored By Matthew Allum + * + * Copyright (C) 2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_UTIL_H +#define __COGL_UTIL_H + +int +cogl_util_next_p2 (int a); + +#endif /* __COGL_UTIL_H */ diff --git a/clutter/cogl/gles/cogl.c b/clutter/cogl/gles/cogl.c index 495702aee..e43908893 100644 --- a/clutter/cogl/gles/cogl.c +++ b/clutter/cogl/gles/cogl.c @@ -32,16 +32,12 @@ #include #include -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -#define PIXEL_TYPE GL_UNSIGNED_BYTE -#else -#define PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV -#endif +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-context.h" -static gulong __enable_flags = 0; - -#define COGL_DEBUG 0 +/* GL error to string conversion */ #if COGL_DEBUG struct token_string { @@ -75,20 +71,6 @@ error_string(GLenum errorCode) } #endif -#if COGL_DEBUG -#define GE(x...) { \ - GLenum err; \ - (x); \ - fprintf(stderr, "%s\n", #x); \ - while ((err = glGetError()) != GL_NO_ERROR) { \ - fprintf(stderr, "glError: %s caught at %s:%u\n", \ - (char *)error_string(err), \ - __FILE__, __LINE__); \ - } \ -} -#else -#define GE(x) (x); -#endif CoglFuncPtr cogl_get_proc_address (const gchar* name) @@ -117,9 +99,6 @@ cogl_paint_init (const ClutterColor *color) glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glDisable (GL_LIGHTING); glDisable (GL_FOG); - - cogl_enable (CGL_ENABLE_BLEND); - glTexEnvx (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); } /* FIXME: inline most of these */ @@ -173,53 +152,98 @@ cogl_rotate (gint angle, gint x, gint y, gint z) CLUTTER_INT_TO_FIXED(z)) ); } +static inline gboolean +cogl_toggle_flag (CoglContext *ctx, + gulong new_flags, + gulong flag, + GLenum gl_flag) +{ + /* Toggles and caches a single enable flag on or off + * by comparing to current state + */ + if (new_flags & flag) + { + if (!(ctx->enable_flags & flag)) + { + GE( glEnable (gl_flag) ); + ctx->enable_flags |= flag; + return TRUE; + } + } + else if (ctx->enable_flags & flag) + { + GE( glDisable (gl_flag) ); + ctx->enable_flags &= ~flag; + } + + return FALSE; +} + +static inline gboolean +cogl_toggle_client_flag (CoglContext *ctx, + gulong new_flags, + gulong flag, + GLenum gl_flag) +{ + /* Toggles and caches a single client-side enable flag + * on or off by comparing to current state + */ + if (new_flags & flag) + { + if (!(ctx->enable_flags & flag)) + { + GE( glEnableClientState (gl_flag) ); + ctx->enable_flags |= flag; + return TRUE; + } + } + else if (ctx->enable_flags & flag) + { + GE( glDisableClientState (gl_flag) ); + ctx->enable_flags &= ~flag; + } + + return FALSE; +} + void cogl_enable (gulong flags) { /* This function essentially caches glEnable state() in the * hope of lessening number GL traffic. */ - if (flags & CGL_ENABLE_BLEND) + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (cogl_toggle_flag (ctx, flags, + COGL_ENABLE_BLEND, + GL_BLEND)) { - if (!(__enable_flags & CGL_ENABLE_BLEND)) - { - GE( glEnable (GL_BLEND) ); - GE( glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ); - } - __enable_flags |= CGL_ENABLE_BLEND; - } - else if (__enable_flags & CGL_ENABLE_BLEND) - { - GE( glDisable (GL_BLEND) ); - __enable_flags &= ~CGL_ENABLE_BLEND; + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } + + cogl_toggle_flag (ctx, flags, + COGL_ENABLE_TEXTURE_2D, + GL_TEXTURE_2D); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_VERTEX_ARRAY, + GL_VERTEX_ARRAY); + + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_TEXCOORD_ARRAY, + GL_TEXTURE_COORD_ARRAY); - if (flags & CGL_ENABLE_TEXTURE_2D) - { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_2D)) - GE( glEnable (GL_TEXTURE_2D) ); - __enable_flags |= CGL_ENABLE_TEXTURE_2D; - } - else if (__enable_flags & CGL_ENABLE_TEXTURE_2D) - { - GE( glDisable (GL_TEXTURE_2D) ); - __enable_flags &= ~CGL_ENABLE_TEXTURE_2D; - } + cogl_toggle_client_flag (ctx, flags, + COGL_ENABLE_COLOR_ARRAY, + GL_COLOR_ARRAY); +} -#if 0 - if (flags & CGL_ENABLE_TEXTURE_RECT) - { - if (!(__enable_flags & CGL_ENABLE_TEXTURE_RECT)) - glEnable (GL_TEXTURE_RECTANGLE_); - - __enable_flags |= CGL_ENABLE_TEXTURE_RECT; - } - else if (__enable_flags & CGL_ENABLE_TEXTURE_RECT) - { - glDisable (GL_TEXTURE_RECTANGLE_); - __enable_flags &= ~CGL_ENABLE_TEXTURE_RECT; - } -#endif +gulong +cogl_get_enable () +{ + _COGL_GET_CONTEXT (ctx, 0); + + return ctx->enable_flags; } void @@ -242,6 +266,8 @@ cogl_enable_depth_test (gboolean setting) void cogl_color (const ClutterColor *color) { + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + #if 0 /*HAVE_GLES_COLOR4UB*/ /* NOTE: seems SDK_OGLES-1.1_LINUX_PCEMULATION_2.02.22.0756 has this call @@ -267,25 +293,9 @@ cogl_color (const ClutterColor *color) (color->blue << 16) / 0xff, (color->alpha << 16) / 0xff)); #endif -} - -static inline void -cogl_rectangle_internal (ClutterFixed x, - ClutterFixed y, - ClutterFixed width, - ClutterFixed height) -{ - GLfixed rect_verts[8] = { - x, y, - x + width, y, - x, y + height, - x + width, y + height - }; - - GE( glEnableClientState (GL_VERTEX_ARRAY) ); - GE( glVertexPointer (2, GL_FIXED, 0, rect_verts) ); - GE( glDrawArrays (GL_TRIANGLE_STRIP, 0, 4) ); - GE( glDisableClientState (GL_VERTEX_ARRAY) ); + + /* Store alpha for proper blending enables */ + ctx->color_alpha = color->alpha; } void @@ -294,199 +304,57 @@ cogl_clip_set (ClutterFixed x_offset, ClutterFixed width, ClutterFixed height) { - GE( glEnable (GL_STENCIL_TEST) ); + if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) + { + GLfixed eqn_left[4] = { CFX_ONE, 0, 0, -x_offset }; + GLfixed eqn_right[4] = { -CFX_ONE, 0, 0, x_offset + width }; + GLfixed eqn_top[4] = { 0, CFX_ONE, 0, -y_offset }; + GLfixed eqn_bottom[4] = { 0, -CFX_ONE, 0, y_offset + height }; - GE( glClearStencil (0) ); - GE( glClear (GL_STENCIL_BUFFER_BIT) ); + GE( glClipPlanex (GL_CLIP_PLANE0, eqn_left) ); + GE( glClipPlanex (GL_CLIP_PLANE1, eqn_right) ); + GE( glClipPlanex (GL_CLIP_PLANE2, eqn_top) ); + GE( glClipPlanex (GL_CLIP_PLANE3, eqn_bottom) ); + GE( glEnable (GL_CLIP_PLANE0) ); + GE( glEnable (GL_CLIP_PLANE1) ); + GE( glEnable (GL_CLIP_PLANE2) ); + GE( glEnable (GL_CLIP_PLANE3) ); + } + else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) + { + GE( glEnable (GL_STENCIL_TEST) ); - GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); - GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); + GE( glClearStencil (0) ); + GE( glClear (GL_STENCIL_BUFFER_BIT) ); - GE( glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) ); + GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); + GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); - cogl_rectangle_internal (x_offset, y_offset, width, height); + GE( glColor4x (CFX_ONE, CFX_ONE, CFX_ONE, CFX_ONE ) ); - GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); - GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + cogl_fast_fill_rectanglex (x_offset, y_offset, width, height); + + GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); + GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); + } } void cogl_clip_unset (void) { - GE( glDisable (GL_STENCIL_TEST) ); + if (cogl_features_available (COGL_FEATURE_FOUR_CLIP_PLANES)) + { + GE( glDisable (GL_CLIP_PLANE3) ); + GE( glDisable (GL_CLIP_PLANE2) ); + GE( glDisable (GL_CLIP_PLANE1) ); + GE( glDisable (GL_CLIP_PLANE0) ); + } + else if (cogl_features_available (COGL_FEATURE_STENCIL_BUFFER)) + { + GE( glDisable (GL_STENCIL_TEST) ); + } } -gboolean -cogl_texture_can_size (COGLenum target, - COGLenum pixel_format, - COGLenum pixel_type, - int width, - int height) -{ - /* FIXME: How we get this is likely GLES implementation dependant. */ - return TRUE; -} - -void -cogl_texture_quad (gint x1, - gint x2, - gint y1, - gint y2, - ClutterFixed tx1, - ClutterFixed ty1, - ClutterFixed tx2, - ClutterFixed ty2) -{ -#define FIX CLUTTER_INT_TO_FIXED - - GLfixed quadVerts[] = { - FIX(x1), FIX(y1), 0, - FIX(x2), FIX(y1), 0, - FIX(x2), FIX(y2), 0, - FIX(x2), FIX(y2), 0, - FIX(x1), FIX(y2), 0, - FIX(x1), FIX(y1), 0 - }; - - GLfixed quadTex[] = { - tx1, ty1, - tx2, ty1, - tx2, ty2, - tx2, ty2, - tx1, ty2, - tx1, ty1 - }; - -#undef FIX - - GE( glEnableClientState(GL_VERTEX_ARRAY) ); - GE( glEnableClientState(GL_TEXTURE_COORD_ARRAY) ); - GE( glVertexPointer(3, GL_FIXED, 0, quadVerts) ); - GE( glTexCoordPointer(2, GL_FIXED, 0, quadTex) ); - GE( glDrawArrays(GL_TRIANGLES, 0, 6) ); - GE( glDisableClientState(GL_TEXTURE_COORD_ARRAY) ); - GE( glDisableClientState(GL_VERTEX_ARRAY) ); - - /* Note also see glDrawTexxOES for potential optimisation */ -} - -void -cogl_textures_create (guint num, COGLuint *textures) -{ - GE( glGenTextures (num, textures) ); -} - -void -cogl_textures_destroy (guint num, const COGLuint *textures) -{ - GE( glDeleteTextures (num, textures) ); -} - -void -cogl_texture_bind (COGLenum target, COGLuint texture) -{ - GE( glBindTexture (target, texture) ); -} - -void -cogl_texture_set_alignment (COGLenum target, - guint alignment, - guint row_length) -{ - /* GE( glPixelStorei (GL_UNPACK_ROW_LENGTH, row_length) ); */ - GE( glPixelStorei (GL_UNPACK_ALIGNMENT, alignment) ); -} - -void -cogl_texture_set_filters (COGLenum target, - COGLenum min_filter, - COGLenum max_filter) -{ - GE( glTexParameteri(target, GL_TEXTURE_MAG_FILTER, max_filter) ); - GE( glTexParameteri(target, GL_TEXTURE_MIN_FILTER, min_filter) ); -} - -void -cogl_texture_set_wrap (COGLenum target, - COGLenum wrap_s, - COGLenum wrap_t) -{ - GE( glTexParameteri(target, GL_TEXTURE_WRAP_S, wrap_s) ); - GE( glTexParameteri(target, GL_TEXTURE_WRAP_T, wrap_s) ); -} - -void -cogl_texture_image_2d (COGLenum target, - COGLint internal_format, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels) -{ - GE( glTexImage2D (target, - 0, - format, /* HACK: For gles we set the internal_format equal - * to the pixel format. This is for RGB data (i.e - * jpgs) which seem to need a matching internal - * format rather than RGBA (which is used by GL) - *. - * This fix isn't ideal.. - */ - width, - height, - 0, - format, - type, - pixels) ); -} - -void -cogl_texture_sub_image_2d (COGLenum target, - gint xoff, - gint yoff, - gint width, - gint height, - COGLenum format, - COGLenum type, - const guchar* pixels) -{ - GE( glTexSubImage2D (target, - 0, - xoff, - yoff, - width, - height, - format, - type, - pixels)); -} - -void -cogl_rectangle (gint x, - gint y, - guint width, - guint height) -{ - cogl_rectangle_internal (CLUTTER_INT_TO_FIXED (x), - CLUTTER_INT_TO_FIXED (y), - CLUTTER_INT_TO_FIXED (width), - CLUTTER_INT_TO_FIXED (height)); -} - -/* FIXME: Should use ClutterReal or Fixed */ -void -cogl_trapezoid (gint y1, - gint x11, - gint x21, - gint y2, - gint x12, - gint x22) -{ - /* FIXME */ -} - - void cogl_alpha_func (COGLenum func, ClutterFixed ref) @@ -591,11 +459,49 @@ cogl_setup_viewport (guint w, GE( glTranslatex (0, -CFX_ONE * height, 0) ); } +static void +_cogl_features_init () +{ + ClutterFeatureFlags flags = 0; + int stencil_bits = 0; + int max_clip_planes = 0; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + flags = COGL_FEATURE_TEXTURE_READ_PIXELS; + + GE( glGetIntegerv (GL_STENCIL_BITS, &stencil_bits) ); + if (stencil_bits > 0) + flags |= COGL_FEATURE_STENCIL_BUFFER; + + GE( glGetIntegerv (GL_MAX_CLIP_PLANES, &max_clip_planes) ); + if (max_clip_planes >= 4) + flags |= COGL_FEATURE_FOUR_CLIP_PLANES; + + ctx->feature_flags = flags; + ctx->features_cached = TRUE; +} + ClutterFeatureFlags cogl_get_features () { - /* Suck */ - return 0; + _COGL_GET_CONTEXT (ctx, 0); + + if (!ctx->features_cached) + _cogl_features_init (); + + return ctx->feature_flags; +} + +gboolean +cogl_features_available (CoglFeatureFlags features) +{ + _COGL_GET_CONTEXT (ctx, 0); + + if (!ctx->features_cached) + _cogl_features_init (); + + return (ctx->feature_flags & features) == features; } void @@ -654,39 +560,6 @@ cogl_fog_set (const ClutterColor *fog_color, glFogx (GL_FOG_END, (GLfixed) z_far); } -/* Offscreen - TODO: possible support from FBO's/PBuffers - * See; - * http://www.khronos.org/message_boards/viewtopic.php?t=589 - * http://www.gamedev.net/community/forums/topic.asp?topic_id=369739 - * - * Likely requires EGL 1.3 for eglBindTexImage -*/ -COGLuint -cogl_offscreen_create (COGLuint target_texture) -{ - return 0; -} - -void -cogl_offscreen_destroy (COGLuint offscreen_handle) -{ -} - -void -cogl_offscreen_redirect_start (COGLuint offscreen_handle, - gint width, - gint height) -{ -} - -void -cogl_offscreen_redirect_end (COGLuint offscreen_handle, - gint width, - gint height) -{ -} - - /* Shaders, no support on regular OpenGL 1.1 */ COGLhandle diff --git a/clutter/eglnative/clutter-backend-egl.c b/clutter/eglnative/clutter-backend-egl.c index 88e916ef1..889bc1d8c 100644 --- a/clutter/eglnative/clutter-backend-egl.c +++ b/clutter/eglnative/clutter-backend-egl.c @@ -52,40 +52,7 @@ static void clutter_backend_egl_ensure_context (ClutterBackend *backend, ClutterStage *stage) { - ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); - - if (stage == NULL) - { - CLUTTER_NOTE (BACKEND, "Clearing EGL context"); - eglMakeCurrent (backend_egl->edpy, - EGL_NO_SURFACE, - EGL_NO_SURFACE, - EGL_NO_CONTEXT); - } - else - { - ClutterStageWindow *impl; - ClutterStageEGL *stage_egl; - - impl = _clutter_stage_get_window (stage); - g_assert (impl != NULL); - - stage_egl = CLUTTER_STAGE_EGL (impl); - - if (!backend_egl->egl_context) - return; - - if (stage_egl->egl_surface == EGL_NO_SURFACE) - eglMakeCurrent (backend_egl->edpy, - EGL_NO_SURFACE, - EGL_NO_SURFACE, - EGL_NO_CONTEXT); - else - eglMakeCurrent (backend_egl->edpy, - stage_egl->egl_surface, - stage_egl->egl_surface, - backend_egl->egl_context); - } + /* not doing anything since we only have one context */ } static void @@ -101,15 +68,11 @@ clutter_backend_egl_redraw (ClutterBackend *backend, return; g_assert (CLUTTER_IS_STAGE_EGL (impl)); - stage_egl = CLUTTER_STAGE_EGL (impl); - clutter_actor_paint (CLUTTER_ACTOR (stage_egl)); - - /* Why this paint is done in backend as likely GL windowing system - * specific calls, like swapping buffers. - */ - /* clutter_feature_wait_for_vblank (); */ + eglWaitNative (EGL_CORE_NATIVE_ENGINE); + clutter_actor_paint (CLUTTER_ACTOR (stage)); + eglWaitGL(); eglSwapBuffers (backend_egl->edpy, stage_egl->egl_surface); } @@ -119,8 +82,8 @@ clutter_backend_egl_create_stage (ClutterBackend *backend, GError **error) { ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); - ClutterStageEGL *stage_egl; - ClutterActor *stage; + ClutterStageEGL *stage_egl; + ClutterActor *stage; if (backend_egl->stage) { @@ -149,7 +112,7 @@ clutter_backend_egl_create_stage (ClutterBackend *backend, return NULL; } - backend_egl->stage = stage_egl; + backend_egl->stage = CLUTTER_ACTOR (stage_egl); return stage; } @@ -158,7 +121,6 @@ static void clutter_backend_egl_init_events (ClutterBackend *backend) { _clutter_events_init (backend); - } static const GOptionEntry entries[] = @@ -166,14 +128,6 @@ static const GOptionEntry entries[] = { NULL } }; -static ClutterActor * -clutter_backend_egl_get_stage (ClutterBackend *backend) -{ - ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); - - return backend_egl->stage; -} - static void clutter_backend_egl_finalize (GObject *gobject) { @@ -199,7 +153,7 @@ clutter_backend_egl_dispose (GObject *gobject) if (backend_egl->edpy) { eglTerminate (backend_egl->edpy); - backend_egl->edpy = NULL; + backend_egl->edpy = 0; } G_OBJECT_CLASS (clutter_backend_egl_parent_class)->dispose (gobject); @@ -264,7 +218,7 @@ clutter_backend_egl_class_init (ClutterBackendEGLClass *klass) backend_class->pre_parse = clutter_backend_egl_pre_parse; backend_class->post_parse = clutter_backend_egl_post_parse; backend_class->init_events = clutter_backend_egl_init_events; - backend_class->create_stage = clutter_backend_egl_init_stage; + backend_class->create_stage = clutter_backend_egl_create_stage; backend_class->ensure_context = clutter_backend_egl_ensure_context; backend_class->redraw = clutter_backend_egl_redraw; backend_class->get_features = clutter_backend_egl_get_features; diff --git a/clutter/eglnative/clutter-backend-egl.h b/clutter/eglnative/clutter-backend-egl.h index 806ecf339..13e3c39d5 100644 --- a/clutter/eglnative/clutter-backend-egl.h +++ b/clutter/eglnative/clutter-backend-egl.h @@ -22,12 +22,11 @@ #ifndef __CLUTTER_BACKEND_EGL_H__ #define __CLUTTER_BACKEND_EGL_H__ -#include -#include - #include #include +#include +#include G_BEGIN_DECLS #define CLUTTER_TYPE_BACKEND_EGL (clutter_backend_egl_get_type ()) @@ -40,6 +39,7 @@ G_BEGIN_DECLS typedef struct _ClutterBackendEGL ClutterBackendEGL; typedef struct _ClutterBackendEGLClass ClutterBackendEGLClass; + struct _ClutterBackendEGL { ClutterBackend parent_instance; diff --git a/clutter/eglnative/clutter-egl.h b/clutter/eglnative/clutter-egl.h index 3e591bae2..759d0215d 100644 --- a/clutter/eglnative/clutter-egl.h +++ b/clutter/eglnative/clutter-egl.h @@ -35,8 +35,7 @@ G_BEGIN_DECLS -EGLDisplay -clutter_egl_display (void); +EGLDisplay clutter_egl_display (void); G_END_DECLS diff --git a/clutter/eglnative/clutter-stage-egl.c b/clutter/eglnative/clutter-stage-egl.c index 042c3a480..db9ce75df 100644 --- a/clutter/eglnative/clutter-stage-egl.c +++ b/clutter/eglnative/clutter-stage-egl.c @@ -2,9 +2,9 @@ #include "config.h" #endif -#include "clutter-backend-egl.h" #include "clutter-stage-egl.h" #include "clutter-egl.h" +#include "clutter-backend-egl.h" #include "../clutter-main.h" #include "../clutter-feature.h" @@ -29,12 +29,21 @@ G_DEFINE_TYPE_WITH_CODE (ClutterStageEGL, static void clutter_stage_egl_show (ClutterActor *actor) { + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor); + + CLUTTER_ACTOR_SET_FLAGS (stage_egl, CLUTTER_ACTOR_MAPPED); + CLUTTER_ACTOR_SET_FLAGS (stage_egl->wrapper, CLUTTER_ACTOR_MAPPED); + CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->show (actor); } static void clutter_stage_egl_hide (ClutterActor *actor) { + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor); + + CLUTTER_ACTOR_UNSET_FLAGS (stage_egl, CLUTTER_ACTOR_MAPPED); + CLUTTER_ACTOR_UNSET_FLAGS (stage_egl->wrapper, CLUTTER_ACTOR_MAPPED); CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->hide (actor); } @@ -49,7 +58,7 @@ clutter_stage_egl_unrealize (ClutterActor *actor) if (stage_egl->egl_surface) { - eglDestroySurface (clutter_egl_display(), stage_egl->egl_surface); + eglDestroySurface (clutter_egl_display (), stage_egl->egl_surface); stage_egl->egl_surface = EGL_NO_SURFACE; } @@ -64,7 +73,6 @@ clutter_stage_egl_realize (ClutterActor *actor) EGLConfig configs[2]; EGLint config_count; EGLBoolean status; - ClutterPerspective perspective; gboolean is_offscreen; CLUTTER_NOTE (BACKEND, "Realizing main stage"); @@ -81,7 +89,7 @@ clutter_stage_egl_realize (ClutterActor *actor) EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 16, EGL_ALPHA_SIZE, EGL_DONT_CARE, - EGL_STENCIL_SIZE, EGL_DONT_CARE, + EGL_STENCIL_SIZE, 2, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; @@ -108,12 +116,20 @@ clutter_stage_egl_realize (ClutterActor *actor) stage_egl->egl_surface = EGL_NO_SURFACE; } + if (backend_egl->egl_context) + { + eglDestroyContext (backend_egl->edpy, backend_egl->egl_context); + backend_egl->egl_context = NULL; + } + stage_egl->egl_surface = eglCreateWindowSurface (backend_egl->edpy, configs[0], NULL, NULL); + + if (stage_egl->egl_surface == EGL_NO_SURFACE) { g_critical ("Unable to create an EGL surface"); @@ -136,7 +152,8 @@ clutter_stage_egl_realize (ClutterActor *actor) stage_egl->surface_width, stage_egl->surface_height); - if (G_UNLIKELY (backend_egl->egl_context == None)) + + if (G_UNLIKELY (backend_egl->egl_context == NULL)) { CLUTTER_NOTE (GL, "Creating EGL Context"); @@ -154,11 +171,20 @@ clutter_stage_egl_realize (ClutterActor *actor) } } - /* this will make sure to set the current context */ CLUTTER_NOTE (BACKEND, "Marking stage as realized and setting context"); CLUTTER_ACTOR_SET_FLAGS (stage_egl->wrapper, CLUTTER_ACTOR_REALIZED); CLUTTER_ACTOR_SET_FLAGS (stage_egl, CLUTTER_ACTOR_REALIZED); - clutter_stage_ensure_current (stage_egl->wrapper); + + + /* eglnative can have only one context */ + status = eglMakeCurrent (backend_egl->edpy, + stage_egl->egl_surface, + stage_egl->egl_surface, + backend_egl->egl_context); + + if (status != EGL_TRUE) + g_warning ("eglMakeCurrent"); + } else { @@ -167,6 +193,7 @@ clutter_stage_egl_realize (ClutterActor *actor) return; } + CLUTTER_SET_PRIVATE_FLAGS (stage_egl->wrapper, CLUTTER_ACTOR_SYNC_MATRICES); } @@ -210,18 +237,17 @@ clutter_stage_egl_dispose (GObject *gobject) static void clutter_stage_egl_class_init (ClutterStageEGLClass *klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); - ClutterStageClass *stage_class = CLUTTER_STAGE_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->dispose = clutter_stage_egl_dispose; - actor_class->show = clutter_stage_egl_show; - actor_class->hide = clutter_stage_egl_hide; - actor_class->realize = clutter_stage_egl_realize; - actor_class->unrealize = clutter_stage_egl_unrealize; + actor_class->show = clutter_stage_egl_show; + actor_class->hide = clutter_stage_egl_hide; + actor_class->realize = clutter_stage_egl_realize; + actor_class->unrealize = clutter_stage_egl_unrealize; actor_class->request_coords = clutter_stage_egl_request_coords; - actor_class->query_coords = clutter_stage_egl_query_coords; + actor_class->query_coords = clutter_stage_egl_query_coords; } static void @@ -232,10 +258,18 @@ clutter_stage_egl_set_fullscreen (ClutterStageWindow *stage_window, G_OBJECT_TYPE_NAME (stage_window)); } +static ClutterActor * +clutter_stage_egl_get_wrapper (ClutterStageWindow *stage_window) +{ + return CLUTTER_ACTOR (CLUTTER_STAGE_EGL (stage_window)->wrapper); +} + static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface) { iface->set_fullscreen = clutter_stage_egl_set_fullscreen; + iface->set_title = NULL; + iface->get_wrapper = clutter_stage_egl_get_wrapper; } static void diff --git a/clutter/eglnative/clutter-stage-egl.h b/clutter/eglnative/clutter-stage-egl.h index 1144c8a89..319cb6515 100644 --- a/clutter/eglnative/clutter-stage-egl.h +++ b/clutter/eglnative/clutter-stage-egl.h @@ -3,8 +3,10 @@ #include #include + #include #include +#include "clutter-backend-egl.h" #define CLUTTER_TYPE_STAGE_EGL (clutter_stage_egl_get_type ()) #define CLUTTER_STAGE_EGL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_EGL, ClutterStageEGL)) @@ -27,7 +29,8 @@ struct _ClutterStageEGL EGLSurface egl_surface; /* the stage wrapper */ - ClutterStage *wrapper; + ClutterStage *wrapper; + ClutterBackendEGL *backend; }; struct _ClutterStageEGLClass diff --git a/clutter/eglx/clutter-stage-egl.c b/clutter/eglx/clutter-stage-egl.c index 4cca02243..bfd00c90e 100644 --- a/clutter/eglx/clutter-stage-egl.c +++ b/clutter/eglx/clutter-stage-egl.c @@ -65,7 +65,7 @@ clutter_stage_egl_unrealize (ClutterActor *actor) clutter_stage_ensure_current (stage_x11->wrapper); - XSync (stage_x11->xdpy, False); + XSync (stage_x11->xdpy, False); clutter_x11_untrap_x_errors (); @@ -104,6 +104,47 @@ clutter_stage_egl_realize (ClutterActor *actor) 2, &config_count); + int c; + int num_configs; + EGLConfig *all_configs; + + eglGetConfigs (clutter_eglx_display(), NULL, 0, &num_configs); + + all_configs = g_malloc (num_configs * sizeof (EGLConfig)); + + eglGetConfigs (clutter_eglx_display(), + all_configs, + num_configs, + &num_configs); + + for (c=0; cdispose (gobject); } -static GdkPixbuf * -clutter_stage_egl_draw_to_pixbuf (ClutterStageWindow *stage_window, - gint x, - gint y, - gint width, - gint height) -{ - ClutterStage *stage = CLUTTER_STAGE (stage_window); - - g_warning ("Stages of type `%s' do not support " - "ClutterStageWindow::draw_to_pixbuf", - G_OBJECT_TYPE_NAME (stage)); - return NULL; -} - static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface) { - iface->draw_to_pixbuf = clutter_stage_egl_draw_to_pixbuf; - /* the rest is inherited from ClutterStageX11 */ } diff --git a/clutter/fruity/Makefile.am b/clutter/fruity/Makefile.am new file mode 100644 index 000000000..eded38897 --- /dev/null +++ b/clutter/fruity/Makefile.am @@ -0,0 +1,22 @@ +libclutterincludedir = $(includedir)/clutter-@CLUTTER_MAJORMINOR@/clutter + +INCLUDES = \ + -DG_LOG_DOMAIN=\"ClutterFruity\" \ + -I$(top_srcdir) \ + -I$(top_srcdir)/clutter \ + $(CLUTTER_CFLAGS) \ + $(CLUTTER_DEBUG_CFLAGS) \ + $(GCC_FLAGS) \ + -xobjective-c + +LDADD = $(CLUTTER_LIBS) + +noinst_LTLIBRARIES = libclutter-fruity.la + +libclutter_fruity_la_SOURCES = \ + clutter-backend-fruity.h \ + clutter-backend-fruity.c \ + clutter-stage-fruity.h \ + clutter-stage-fruity.c \ + clutter-fruity.h \ + clutter-fruity.c diff --git a/clutter/fruity/clutter-backend-fruity.c b/clutter/fruity/clutter-backend-fruity.c new file mode 100644 index 000000000..75400bcf4 --- /dev/null +++ b/clutter/fruity/clutter-backend-fruity.c @@ -0,0 +1,257 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-backend-fruity.h" +#include "clutter-stage-fruity.h" +#include "../clutter-private.h" +#include "../clutter-main.h" +#include "../clutter-debug.h" + +static ClutterBackendEGL *backend_singleton = NULL; + + +G_DEFINE_TYPE (ClutterBackendEGL, clutter_backend_egl, CLUTTER_TYPE_BACKEND); + +static gboolean +clutter_backend_egl_pre_parse (ClutterBackend *backend, + GError **error) +{ + return TRUE; +} + +static gboolean +clutter_backend_egl_post_parse (ClutterBackend *backend, + GError **error) +{ + ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL(backend); + EGLBoolean status; + + backend_egl->edpy = eglGetDisplay (EGL_DEFAULT_DISPLAY); + + status = eglInitialize (backend_egl->edpy, + &backend_egl->egl_version_major, + &backend_egl->egl_version_minor); + + if (status != EGL_TRUE) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_BACKEND, + "Unable to Initialize EGL"); + return FALSE; + } + + CLUTTER_NOTE (BACKEND, "EGL Reports version %i.%i", + backend_egl->egl_version_major, + backend_egl->egl_version_minor); + + return TRUE; +} + +static void +clutter_backend_egl_ensure_context (ClutterBackend *backend, + ClutterStage *stage) +{ + /* not doing anything since we only have one context */ +} + +static void +clutter_backend_egl_redraw (ClutterBackend *backend, + ClutterStage *stage) +{ + ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); + ClutterStageEGL *stage_egl; + ClutterStageWindow *impl; + + impl = _clutter_stage_get_window (stage); + if (!impl) + return; + + g_assert (CLUTTER_IS_STAGE_EGL (impl)); + stage_egl = CLUTTER_STAGE_EGL (impl); + + eglWaitNative (EGL_CORE_NATIVE_ENGINE); + clutter_actor_paint (CLUTTER_ACTOR (stage)); + eglWaitGL(); + eglSwapBuffers (backend_egl->edpy, stage_egl->egl_surface); +} + +static ClutterActor * +clutter_backend_egl_create_stage (ClutterBackend *backend, + ClutterStage *wrapper, + GError **error) +{ + ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); + ClutterStageEGL *stage_egl; + ClutterActor *stage; + + if (backend_egl->stage) + { + g_warning ("The EGL native backend does not support multiple stages"); + return backend_egl->stage; + } + + stage = g_object_new (CLUTTER_TYPE_STAGE_FRUITY, NULL); + + stage_egl = CLUTTER_STAGE_EGL (stage); + stage_egl->backend = backend_egl; + stage_egl->wrapper = wrapper; + + _clutter_stage_set_window (wrapper, CLUTTER_STAGE_WINDOW (stage)); + + g_object_set_data (G_OBJECT (stage), "clutter-backend", backend); + + clutter_actor_realize (stage); + + if (!CLUTTER_ACTOR_IS_REALIZED (stage)) + { + g_set_error (error, CLUTTER_INIT_ERROR, + CLUTTER_INIT_ERROR_INTERNAL, + "Unable to realize the main stage"); + g_object_unref (stage); + return NULL; + } + + backend_egl->stage = CLUTTER_ACTOR (stage_egl); + + return stage; +} + + +static void +clutter_backend_egl_init_events (ClutterBackend *backend) +{ +} + +static const GOptionEntry entries[] = +{ + { NULL } +}; + +static void +clutter_backend_egl_finalize (GObject *gobject) +{ + if (backend_singleton) + backend_singleton = NULL; + + G_OBJECT_CLASS (clutter_backend_egl_parent_class)->finalize (gobject); +} + +static void +clutter_backend_egl_dispose (GObject *gobject) +{ + ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (gobject); + + _clutter_events_uninit (CLUTTER_BACKEND (backend_egl)); + + if (backend_egl->egl_context) + { + eglDestroyContext (backend_egl->edpy, backend_egl->egl_context); + backend_egl->egl_context = NULL; + } + + if (backend_egl->edpy) + { + eglTerminate (backend_egl->edpy); + backend_egl->edpy = 0; + } + + G_OBJECT_CLASS (clutter_backend_egl_parent_class)->dispose (gobject); +} + +static GObject * +clutter_backend_egl_constructor (GType gtype, + guint n_params, + GObjectConstructParam *params) +{ + GObjectClass *parent_class; + GObject *retval; + + if (!backend_singleton) + { + parent_class = G_OBJECT_CLASS (clutter_backend_egl_parent_class); + retval = parent_class->constructor (gtype, n_params, params); + + backend_singleton = CLUTTER_BACKEND_EGL (retval); + + return retval; + } + + g_warning ("Attempting to create a new backend object. This should " + "never happen, so we return the singleton instance."); + + return g_object_ref (backend_singleton); +} + +static ClutterFeatureFlags +clutter_backend_egl_get_features (ClutterBackend *backend) +{ + ClutterBackendEGL *backend_egl = CLUTTER_BACKEND_EGL (backend); + + CLUTTER_NOTE (BACKEND, "Checking features\n" + "GL_VENDOR: %s\n" + "GL_RENDERER: %s\n" + "GL_VERSION: %s\n" + "EGL_VENDOR: %s\n" + "EGL_VERSION: %s\n" + "EGL_EXTENSIONS: %s\n", + glGetString (GL_VENDOR), + glGetString (GL_RENDERER), + glGetString (GL_VERSION), + eglQueryString (backend_egl->edpy, EGL_VENDOR), + eglQueryString (backend_egl->edpy, EGL_VERSION), + eglQueryString (backend_egl->edpy, EGL_EXTENSIONS)); + + return CLUTTER_FEATURE_STAGE_STATIC; +} + +static void +clutter_backend_egl_class_init (ClutterBackendEGLClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); + + gobject_class->constructor = clutter_backend_egl_constructor; + gobject_class->dispose = clutter_backend_egl_dispose; + gobject_class->finalize = clutter_backend_egl_finalize; + + backend_class->pre_parse = clutter_backend_egl_pre_parse; + backend_class->post_parse = clutter_backend_egl_post_parse; + backend_class->init_events = clutter_backend_egl_init_events; + backend_class->create_stage = clutter_backend_egl_create_stage; + backend_class->ensure_context = clutter_backend_egl_ensure_context; + backend_class->redraw = clutter_backend_egl_redraw; + backend_class->get_features = clutter_backend_egl_get_features; +} + +static void +clutter_backend_egl_init (ClutterBackendEGL *backend_egl) +{ + ClutterBackend *backend = CLUTTER_BACKEND (backend_egl); + + clutter_backend_set_resolution (backend, 96.0); + clutter_backend_set_double_click_time (backend, 250); + clutter_backend_set_double_click_distance (backend, 5); +} + +GType +_clutter_backend_impl_get_type (void) +{ + return clutter_backend_egl_get_type (); +} + +EGLDisplay +clutter_egl_display (void) +{ + return backend_singleton->edpy; +} + +void *rpl_malloc (size_t allocation) +{ + return g_malloc (allocation); +} + +void rpl_free (void *ptr) +{ + g_free (ptr); +} diff --git a/clutter/fruity/clutter-backend-fruity.h b/clutter/fruity/clutter-backend-fruity.h new file mode 100644 index 000000000..817f8d5eb --- /dev/null +++ b/clutter/fruity/clutter-backend-fruity.h @@ -0,0 +1,82 @@ +/* Clutter. + * An OpenGL based 'interactive canvas' library. + * Authored By Matthew Allum + * Copyright (C) 2006-2007 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CLUTTER_BACKEND_EGL_H__ +#define __CLUTTER_BACKEND_EGL_H__ + +/*#ifdef HAVE_CLUTTER_FRUITY */ +/* extra include needed for the GLES header for arm-apple-darwin */ +#include +/*#endif*/ + + +#include +#include + +#include +#include +G_BEGIN_DECLS + +#define CLUTTER_TYPE_BACKEND_FRUITY (clutter_backend_egl_get_type ()) +#define CLUTTER_BACKEND_EGL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND_FRUITY, ClutterBackendEGL)) +#define CLUTTER_IS_BACKEND_EGL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND_FRUITY)) +#define CLUTTER_BACKEND_EGL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND_FRUITY, ClutterBackendEGLClass)) +#define CLUTTER_IS_BACKEND_EGL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BACKEND_FRUITY)) +#define CLUTTER_BACKEND_EGL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BACKEND_FRUITY, ClutterBackendEGLClass)) + +typedef struct _ClutterBackendEGL ClutterBackendEGL; +typedef struct _ClutterBackendEGLClass ClutterBackendEGLClass; + + +struct _ClutterBackendEGL +{ + ClutterBackend parent_instance; + + /* EGL Specific */ + EGLDisplay edpy; + EGLSurface egl_surface; + EGLContext egl_context; + + gint egl_version_major; + gint egl_version_minor; + + /* main stage singleton */ + ClutterActor *stage; + + /* event source */ + GSource *event_source; + + /*< private >*/ +}; + +struct _ClutterBackendEGLClass +{ + ClutterBackendClass parent_class; +}; + +GType clutter_backend_egl_get_type (void) G_GNUC_CONST; + +void _clutter_events_init (ClutterBackend *backend); +void _clutter_events_uninit (ClutterBackend *backend); + +G_END_DECLS + +#endif /* __CLUTTER_BACKEND_EGL_H__ */ diff --git a/clutter/fruity/clutter-fruity.c b/clutter/fruity/clutter-fruity.c new file mode 100644 index 000000000..32be9b08d --- /dev/null +++ b/clutter/fruity/clutter-fruity.c @@ -0,0 +1,197 @@ +#include +#include "clutter-backend-fruity.h" +#include "clutter-stage-fruity.h" +#include "../clutter-main.h" +#include "../clutter-private.h" +#import + +#import +#import +#include +#include +#include + +#import +#import +#import +#import +#import +#include "clutter-fruity.h" + +static EGLDisplay mEGLDisplay; +static EGLContext mEGLContext; +static EGLSurface mEGLSurface; +static CoreSurfaceBufferRef mScreenSurface; +static gboolean alive = TRUE; + +@interface StageView : UIView +{ +} + +@end + +static CoreSurfaceBufferRef CreateSurface(int w, int h) +{ + int pitch = w * 2, allocSize = 2 * w * h; + char *pixelFormat = "565L"; + CFMutableDictionaryRef dict; + + dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(dict, kCoreSurfaceBufferGlobal, kCFBooleanTrue); + CFDictionarySetValue(dict, kCoreSurfaceBufferMemoryRegion, CFSTR("PurpleGFXMem")); + CFDictionarySetValue(dict, kCoreSurfaceBufferPitch, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pitch)); + CFDictionarySetValue(dict, kCoreSurfaceBufferWidth, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &w)); + CFDictionarySetValue(dict, kCoreSurfaceBufferHeight, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &h)); + CFDictionarySetValue(dict, kCoreSurfaceBufferPixelFormat, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, pixelFormat)); + CFDictionarySetValue(dict, kCoreSurfaceBufferAllocSize, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &allocSize)); + return CoreSurfaceBufferCreate(dict); +} + + +@implementation StageView + +- (void) mouseDown:(GSEvent*)event +{ + CGPoint location= GSEventGetLocationInWindow(event); + ClutterBackendEGL *backend_fruity = CLUTTER_BACKEND_EGL (clutter_get_default_backend()); + ClutterStage *stage = CLUTTER_STAGE_EGL(backend_fruity->stage)->wrapper; + ClutterEvent *cev; + float x = location.x; + float y = location.y; + + cev = clutter_event_new (CLUTTER_BUTTON_PRESS); + cev->button.x = x; + cev->button.y = y; + cev->button.button = 1; + cev->button.time = clutter_get_timestamp () / 1000; + cev->any.stage = stage; + + clutter_do_event (cev); + clutter_event_free (cev); +} + +- (void) mouseUp:(GSEvent*)event +{ + ClutterEvent *cev; + ClutterBackendEGL *backend_fruity = CLUTTER_BACKEND_EGL (clutter_get_default_backend()); + ClutterStage *stage = CLUTTER_STAGE_EGL(backend_fruity->stage)->wrapper; + CGPoint location= GSEventGetLocationInWindow(event); + float x = location.x; + float y = location.y; + + cev = clutter_event_new (CLUTTER_BUTTON_RELEASE); + cev->button.x = x; + cev->button.y = y; + cev->button.button = 1; + cev->button.time = clutter_get_timestamp () / 1000; + cev->any.stage = stage; + clutter_do_event (cev); + clutter_event_free (cev); +} + +- (void) mouseDragged:(GSEvent*)event +{ + ClutterEvent *cev; + ClutterBackendEGL *backend_fruity = CLUTTER_BACKEND_EGL (clutter_get_default_backend()); + ClutterStage *stage = CLUTTER_STAGE_EGL(backend_fruity->stage)->wrapper; + CGPoint location= GSEventGetLocationInWindow(event); + float x = location.x; + float y = location.y; + + cev = clutter_event_new (CLUTTER_MOTION); + cev->motion.x = x; + cev->motion.y = y; + cev->motion.time = clutter_get_timestamp () / 1000; + cev->any.stage = stage; + clutter_do_event (cev); + clutter_event_free (cev); +} + +@end + + +@interface ClutterUIKit : UIApplication +{ + StageView *stage_view; +} +@end + +@implementation ClutterUIKit + +- (void) applicationDidFinishLaunching: (id) unused +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + [UIHardware _setStatusBarHeight:0.0f]; + [self setStatusBarMode:2 orientation:0 duration:0.0f fenceID:0]; + + CGRect screenRect = [UIHardware fullScreenApplicationContentRect]; + UIWindow* window = [[UIWindow alloc] initWithContentRect: screenRect]; + + [window orderFront: self]; + [window makeKey: self]; + [window _setHidden: NO]; + + [NSTimer + scheduledTimerWithTimeInterval:0.0025 + target: self + selector: @selector(update) + userInfo: nil + repeats: YES + ]; + + StageView *stageView = [StageView alloc]; + [stageView initWithFrame: screenRect]; + [window setContentView: stageView]; + + stage_view = stageView; + + [pool release]; +} + +- (void)applicationWillTerminate +{ + /* FIXME: here we should do things to shut down the uikit application */ + [stage_view release]; + ClutterBackendEGL *backend_fruity = CLUTTER_BACKEND_EGL (clutter_get_default_backend()); + ClutterStageEGL *stage_fruity; + stage_fruity = CLUTTER_STAGE_EGL(backend_fruity->stage); + + alive = FALSE; + clutter_actor_unrealize (CLUTTER_ACTOR (stage_fruity)); + clutter_main_quit (); +} + +- (void)applicationWillSuspend +{ + ClutterBackendEGL *backend_fruity = CLUTTER_BACKEND_EGL (clutter_get_default_backend()); + ClutterStageEGL *stage_fruity; + stage_fruity = CLUTTER_STAGE_EGL(backend_fruity->stage); + alive = FALSE; +} + +- (void)applicationDidResumeFromUnderLock +{ + alive = TRUE; + [stage_view setNeedsDisplay]; +} + +- (void) update +{ + if (alive && g_main_context_pending (NULL)) + g_main_context_iteration (NULL, FALSE); +} + +@end + +void clutter_fruity_main (void) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + UIApplicationMain(0, NULL, [ClutterUIKit class]); + [pool release]; +} + + + + + diff --git a/clutter/fruity/clutter-fruity.h b/clutter/fruity/clutter-fruity.h new file mode 100644 index 000000000..2af7b981f --- /dev/null +++ b/clutter/fruity/clutter-fruity.h @@ -0,0 +1,48 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Matthew Allum + * + * Copyright (C) 2006 OpenedHand + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __CLUTTER_FRUITY_H__ +#define __CLUTTER_FRUITY_H__ + +#include + +#if HAVE_CLUTTER_FRUITY +/* extra include needed for the GLES header for arm-apple-darwin */ +#include +#endif + +#include +#include + +#include + +G_BEGIN_DECLS + +EGLDisplay clutter_egl_display (void); +void clutter_uikit_main (void); + +G_END_DECLS + +#endif /* __CLUTTER_EGL_H__ */ diff --git a/clutter/fruity/clutter-stage-fruity.c b/clutter/fruity/clutter-stage-fruity.c new file mode 100644 index 000000000..5050651b0 --- /dev/null +++ b/clutter/fruity/clutter-stage-fruity.c @@ -0,0 +1,279 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "clutter-stage-fruity.h" +#include "clutter-fruity.h" +#include "clutter-backend-fruity.h" + +#include "../clutter-main.h" +#include "../clutter-feature.h" +#include "../clutter-color.h" +#include "../clutter-util.h" +#include "../clutter-event.h" +#include "../clutter-enum-types.h" +#include "../clutter-private.h" +#include "../clutter-debug.h" +#include "../clutter-units.h" +#include "../clutter-stage.h" +#include "../clutter-stage-window.h" + +static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ClutterStageEGL, + clutter_stage_egl, + CLUTTER_TYPE_ACTOR, + G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, + clutter_stage_window_iface_init)); + +static void +clutter_stage_egl_show (ClutterActor *actor) +{ + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor); + + CLUTTER_ACTOR_SET_FLAGS (stage_egl, CLUTTER_ACTOR_MAPPED); + CLUTTER_ACTOR_SET_FLAGS (stage_egl->wrapper, CLUTTER_ACTOR_MAPPED); + + CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->show (actor); +} + +static void +clutter_stage_egl_hide (ClutterActor *actor) +{ + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor); + + CLUTTER_ACTOR_UNSET_FLAGS (stage_egl, CLUTTER_ACTOR_MAPPED); + CLUTTER_ACTOR_UNSET_FLAGS (stage_egl->wrapper, CLUTTER_ACTOR_MAPPED); + CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->hide (actor); +} + +static void +clutter_stage_egl_unrealize (ClutterActor *actor) +{ + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor); + + CLUTTER_MARK(); + + CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->unrealize (actor); + + if (stage_egl->egl_surface) + { + eglDestroySurface (clutter_egl_display (), stage_egl->egl_surface); + stage_egl->egl_surface = EGL_NO_SURFACE; + } + + clutter_stage_ensure_current (stage_egl->wrapper); +} + +static void +clutter_stage_egl_realize (ClutterActor *actor) +{ + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (actor); + ClutterBackendEGL *backend_egl; + EGLConfig configs[2]; + EGLint config_count; + EGLBoolean status; + gboolean is_offscreen; + + CLUTTER_NOTE (BACKEND, "Realizing main stage"); + + g_object_get (stage_egl->wrapper, "offscreen", &is_offscreen, NULL); + + backend_egl = CLUTTER_BACKEND_EGL (clutter_get_default_backend ()); + + if (G_LIKELY (!is_offscreen)) + { + EGLint cfg_attribs[] = { EGL_BUFFER_SIZE, EGL_DONT_CARE, + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, + EGL_DEPTH_SIZE, 16, + EGL_ALPHA_SIZE, EGL_DONT_CARE, + EGL_STENCIL_SIZE, 2, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE }; + + status = eglGetConfigs (backend_egl->edpy, + configs, + 2, + &config_count); + + if (status != EGL_TRUE) + g_warning ("eglGetConfigs failed"); + + status = eglChooseConfig (backend_egl->edpy, + cfg_attribs, + configs, + G_N_ELEMENTS (configs), + &config_count); + + if (status != EGL_TRUE) + g_warning ("eglChooseConfig failed"); + + if (stage_egl->egl_surface != EGL_NO_SURFACE) + { + eglDestroySurface (backend_egl->edpy, stage_egl->egl_surface); + stage_egl->egl_surface = EGL_NO_SURFACE; + } + + if (backend_egl->egl_context) + { + eglDestroyContext (backend_egl->edpy, backend_egl->egl_context); + backend_egl->egl_context = NULL; + } + + stage_egl->egl_surface = + eglCreateWindowSurface (backend_egl->edpy, + configs[0], + NULL, + NULL); + + + + if (stage_egl->egl_surface == EGL_NO_SURFACE) + { + g_critical ("Unable to create an EGL surface"); + + CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); + return; + } + + eglQuerySurface (backend_egl->edpy, + stage_egl->egl_surface, + EGL_WIDTH, + &stage_egl->surface_width); + + eglQuerySurface (backend_egl->edpy, + stage_egl->egl_surface, + EGL_HEIGHT, + &stage_egl->surface_height); + + CLUTTER_NOTE (BACKEND, "EGL surface is %ix%i", + stage_egl->surface_width, + stage_egl->surface_height); + + + if (G_UNLIKELY (backend_egl->egl_context == NULL)) + { + CLUTTER_NOTE (GL, "Creating EGL Context"); + + backend_egl->egl_context = eglCreateContext (backend_egl->edpy, + configs[0], + EGL_NO_CONTEXT, + NULL); + + if (backend_egl->egl_context == EGL_NO_CONTEXT) + { + g_critical ("Unable to create a suitable EGL context"); + + CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); + return; + } + } + + /* this will make sure to set the current context */ + CLUTTER_NOTE (BACKEND, "Marking stage as realized and setting context"); + CLUTTER_ACTOR_SET_FLAGS (stage_egl->wrapper, CLUTTER_ACTOR_REALIZED); + CLUTTER_ACTOR_SET_FLAGS (stage_egl, CLUTTER_ACTOR_REALIZED); + + + status = eglMakeCurrent (backend_egl->edpy, + stage_egl->egl_surface, + stage_egl->egl_surface, + backend_egl->egl_context); + + if (status != EGL_TRUE) + g_warning ("eglMakeCurrent"); + + /*clutter_stage_ensure_current (stage_egl->wrapper);*/ + } + else + { + g_warning("EGL Backend does not yet support offscreen rendering\n"); + CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); + return; + } + + + CLUTTER_SET_PRIVATE_FLAGS (stage_egl->wrapper, CLUTTER_ACTOR_SYNC_MATRICES); +} + +static void +clutter_stage_egl_query_coords (ClutterActor *self, + ClutterActorBox *box) +{ + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (self); + + box->x1 = box->y1 = 0; + box->x2 = box->x1 + CLUTTER_UNITS_FROM_INT (stage_egl->surface_width); + box->y2 = box->y1 + CLUTTER_UNITS_FROM_INT (stage_egl->surface_height); +} + +static void +clutter_stage_egl_request_coords (ClutterActor *self, + ClutterActorBox *box) +{ + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (self); + + /* framebuffer no resize */ + box->x1 = 0; + box->y1 = 0; + box->x2 = CLUTTER_UNITS_FROM_INT (stage_egl->surface_width); + box->y2 = CLUTTER_UNITS_FROM_INT (stage_egl->surface_height); + + CLUTTER_ACTOR_CLASS (clutter_stage_egl_parent_class)->request_coords (self, + box); +} + +static void +clutter_stage_egl_dispose (GObject *gobject) +{ + ClutterStageEGL *stage_egl = CLUTTER_STAGE_EGL (gobject); + + clutter_actor_unrealize (CLUTTER_ACTOR (stage_egl)); + + G_OBJECT_CLASS (clutter_stage_egl_parent_class)->dispose (gobject); +} + +static void +clutter_stage_egl_class_init (ClutterStageEGLClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->dispose = clutter_stage_egl_dispose; + + actor_class->show = clutter_stage_egl_show; + actor_class->hide = clutter_stage_egl_hide; + actor_class->realize = clutter_stage_egl_realize; + actor_class->unrealize = clutter_stage_egl_unrealize; + actor_class->request_coords = clutter_stage_egl_request_coords; + actor_class->query_coords = clutter_stage_egl_query_coords; +} + +static void +clutter_stage_egl_set_fullscreen (ClutterStageWindow *stage_window, + gboolean fullscreen) +{ + g_warning ("Stage of type `%s' do not support ClutterStage::set_fullscreen", + G_OBJECT_TYPE_NAME (stage_window)); +} + +static ClutterActor * +clutter_stage_egl_get_wrapper (ClutterStageWindow *stage_window) +{ + return CLUTTER_ACTOR (CLUTTER_STAGE_EGL (stage_window)->wrapper); +} + +static void +clutter_stage_window_iface_init (ClutterStageWindowIface *iface) +{ + iface->set_fullscreen = clutter_stage_egl_set_fullscreen; + iface->set_title = NULL; + iface->get_wrapper = clutter_stage_egl_get_wrapper; +} + +static void +clutter_stage_egl_init (ClutterStageEGL *stage) +{ +} diff --git a/clutter/fruity/clutter-stage-fruity.h b/clutter/fruity/clutter-stage-fruity.h new file mode 100644 index 000000000..c361fbe25 --- /dev/null +++ b/clutter/fruity/clutter-stage-fruity.h @@ -0,0 +1,48 @@ +#ifndef __CLUTTER_STAGE_EGL_H__ +#define __CLUTTER_STAGE_EGL_H__ + +#include +#include + +#if HAVE_CLUTTER_FRUITY +/* extra include needed for the GLES header for arm-apple-darwin */ +#include +#endif + +#include +#include +#include "clutter-backend-fruity.h" + +#define CLUTTER_TYPE_STAGE_FRUITY (clutter_stage_egl_get_type ()) +#define CLUTTER_STAGE_EGL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_FRUITY, ClutterStageEGL)) +#define CLUTTER_IS_STAGE_EGL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_FRUITY)) +#define CLUTTER_STAGE_EGL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_FRUITY, ClutterStageEGLClass)) +#define CLUTTER_IS_STAGE_EGL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_FRUITY)) +#define CLUTTER_STAGE_EGL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_FRUITY, ClutterStageEGLClass)) + +typedef struct _ClutterStageEGL ClutterStageEGL; +typedef struct _ClutterStageEGLClass ClutterStageEGLClass; + +struct _ClutterStageEGL +{ + ClutterActor parent_instance; + + /* from the backend */ + gint surface_width; + gint surface_height; + + EGLSurface egl_surface; + + /* the stage wrapper */ + ClutterStage *wrapper; + ClutterBackendEGL *backend; +}; + +struct _ClutterStageEGLClass +{ + ClutterActorClass parent_class; +}; + +GType clutter_stage_egl_get_type (void) G_GNUC_CONST; + +#endif /* __CLUTTER_STAGE_EGL_H__ */ diff --git a/clutter/glx/Makefile.am b/clutter/glx/Makefile.am index b07263971..182825a17 100644 --- a/clutter/glx/Makefile.am +++ b/clutter/glx/Makefile.am @@ -4,8 +4,8 @@ libclutterinclude_HEADERS = clutter-glx.h clutter-glx-texture-pixmap.h INCLUDES = \ -DG_LOG_DOMAIN=\"ClutterGLX\" \ -I$(top_srcdir) \ - -I$(top_srcdir)/clutter/cogl \ - -I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ \ + -I$(top_srcdir)/clutter \ + -I$(top_builddir)/clutter \ $(CLUTTER_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) diff --git a/clutter/glx/clutter-backend-glx.c b/clutter/glx/clutter-backend-glx.c index f67042e9f..5b738c1ca 100644 --- a/clutter/glx/clutter-backend-glx.c +++ b/clutter/glx/clutter-backend-glx.c @@ -47,7 +47,7 @@ #include "../clutter-private.h" #include "../clutter-version.h" -#include "cogl.h" +#include "cogl/cogl.h" G_DEFINE_TYPE (ClutterBackendGLX, clutter_backend_glx, CLUTTER_TYPE_BACKEND_X11); diff --git a/clutter/glx/clutter-glx-texture-pixmap.c b/clutter/glx/clutter-glx-texture-pixmap.c index 420e551f1..a5c3b43af 100644 --- a/clutter/glx/clutter-glx-texture-pixmap.c +++ b/clutter/glx/clutter-glx-texture-pixmap.c @@ -46,7 +46,7 @@ #include "../clutter-util.h" -#include "cogl.h" +#include "cogl/cogl.h" typedef void (*BindTexImage) (Display *display, GLXDrawable drawable, @@ -159,10 +159,11 @@ clutter_glx_texture_pixmap_init (ClutterGLXTexturePixmap *self) CLUTTER_GLX_TYPE_TEXTURE_PIXMAP, ClutterGLXTexturePixmapPrivate); + /* FIXME: Obsolete. Move to new cogl api. if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE)) priv->target_type = CGL_TEXTURE_RECTANGLE_ARB; else - priv->target_type = CGL_TEXTURE_2D; + priv->target_type = CGL_TEXTURE_2D; */ } static void @@ -224,15 +225,16 @@ clutter_glx_texture_pixmap_realize (ClutterActor *actor) "pixmap-width", &width, "pixmap-height", &height, NULL); - + + /* FIXME: Obsolete. Move to new cogl api cogl_textures_create (1, &priv->texture_id); - + */ clutter_glx_texture_pixmap_update_area (CLUTTER_X11_TEXTURE_PIXMAP (actor), 0, 0, width, height); - + /* cogl_texture_set_alignment (priv->target_type, 4, width); - + cogl_texture_set_filters (priv->target_type, filter_quality ? CGL_LINEAR : CGL_NEAREST, filter_quality ? CGL_LINEAR : CGL_NEAREST); @@ -240,7 +242,7 @@ clutter_glx_texture_pixmap_realize (ClutterActor *actor) cogl_texture_set_wrap (priv->target_type, repeat_x ? CGL_REPEAT : CGL_CLAMP_TO_EDGE, repeat_y ? CGL_REPEAT : CGL_CLAMP_TO_EDGE); - + */ CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); } @@ -274,9 +276,10 @@ clutter_glx_texture_pixmap_unrealize (ClutterActor *actor) XSync (clutter_x11_get_default_display(), FALSE); clutter_x11_untrap_x_errors (); } - + + /* FIXME: Obsolete. Move to new cogl api. cogl_textures_destroy (1, &priv->texture_id); - + */ CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); } @@ -287,6 +290,7 @@ texture_render_to_gl_quad (ClutterGLXTexturePixmap *texture, int x_2, int y_2) { + /* FIXME: Obsolete. Move to new cogl api ClutterGLXTexturePixmapPrivate *priv = texture->priv; int qx1 = 0, qx2 = 0, qy1 = 0, qy2 = 0; @@ -304,7 +308,7 @@ texture_render_to_gl_quad (ClutterGLXTexturePixmap *texture, cogl_texture_bind (priv->target_type, priv->texture_id); - if (priv->target_type == CGL_TEXTURE_2D) /* POT */ + if (priv->target_type == CGL_TEXTURE_2D) { tx = (float) width / clutter_util_next_p2 (width); ty = (float) height / clutter_util_next_p2 (height); @@ -323,7 +327,7 @@ texture_render_to_gl_quad (ClutterGLXTexturePixmap *texture, 0, 0, CLUTTER_FLOAT_TO_FIXED (tx), - CLUTTER_FLOAT_TO_FIXED (ty)); + CLUTTER_FLOAT_TO_FIXED (ty)); */ } static void @@ -347,6 +351,7 @@ clutter_glx_texture_pixmap_paint (ClutterActor *actor) cogl_push_matrix (); + /* FIXME: Obsolete. Move to new cogl api. switch (priv->target_type) { case CGL_TEXTURE_2D: @@ -357,7 +362,7 @@ clutter_glx_texture_pixmap_paint (ClutterActor *actor) break; default: break; - } + } */ col.alpha = clutter_actor_get_opacity (actor); @@ -527,11 +532,12 @@ clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) attribs[i++] = GLX_TEXTURE_TARGET_EXT; + /* FIXME: Obsolete. Move to new cogl api if (priv->target_type == CGL_TEXTURE_RECTANGLE_ARB) attribs[i++] = GLX_TEXTURE_RECTANGLE_EXT; else - attribs[i++] = GLX_TEXTURE_2D_EXT; - + attribs[i++] = GLX_TEXTURE_2D_EXT; */ + attribs[i++] = None; @@ -553,8 +559,9 @@ clutter_glx_texture_pixmap_create_glx_pixmap (ClutterGLXTexturePixmap *texture) CLUTTER_ACTOR_IS_REALIZED (texture) && priv->bound) { - cogl_texture_bind (priv->target_type, priv->texture_id); - + /* + cogl_texture_bind (priv->target_type, priv->texture_id); */ + clutter_x11_trap_x_errors (); (_gl_release_tex_image) (dpy, @@ -603,7 +610,8 @@ clutter_glx_texture_pixmap_update_area (ClutterX11TexturePixmap *texture, if (!CLUTTER_ACTOR_IS_REALIZED (texture)) return; - cogl_texture_bind (priv->target_type, priv->texture_id); + /* FIXME: Obsolete. + cogl_texture_bind (priv->target_type, priv->texture_id); */ if (_have_tex_from_pixmap_ext) { diff --git a/clutter/glx/clutter-stage-glx.c b/clutter/glx/clutter-stage-glx.c index 50589ccca..4665d585f 100644 --- a/clutter/glx/clutter-stage-glx.c +++ b/clutter/glx/clutter-stage-glx.c @@ -42,13 +42,11 @@ #include "../clutter-stage.h" #include "../clutter-stage-window.h" -#include "cogl.h" +#include "cogl/cogl.h" #include #include -#include - static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterStageGLX, @@ -313,85 +311,6 @@ fail: CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REALIZED); } -static void -snapshot_pixbuf_free (guchar *pixels, - gpointer data) -{ - g_free (pixels); -} - -static GdkPixbuf * -clutter_stage_glx_draw_to_pixbuf (ClutterStageWindow *stage_window, - gint x, - gint y, - gint width, - gint height) -{ - guchar *data; - GdkPixbuf *pixb; - ClutterActor *actor; - ClutterStageGLX *stage_glx; - ClutterStageX11 *stage_x11; - gboolean is_offscreen = FALSE; - - stage_glx = CLUTTER_STAGE_GLX (stage_window); - stage_x11 = CLUTTER_STAGE_X11 (stage_window); - actor = CLUTTER_ACTOR (stage_window); - - if (width < 0) - width = clutter_actor_get_width (actor); - - if (height < 0) - height = clutter_actor_get_height (actor); - - g_object_get (stage_x11->wrapper, "offscreen", &is_offscreen, NULL); - - if (G_UNLIKELY (is_offscreen)) - { - gdk_pixbuf_xlib_init (stage_x11->xdpy, stage_x11->xscreen); - - pixb = gdk_pixbuf_xlib_get_from_drawable (NULL, - (Drawable) stage_x11->xpixmap, - DefaultColormap (stage_x11->xdpy, - stage_x11->xscreen), - stage_x11->xvisinfo->visual, - x, y, - 0, 0, - width, height); - } - else - { - GdkPixbuf *tmp = NULL, *tmp2 = NULL; - gint stride; - - stride = ((width * 4 + 3) &~ 3); - - data = g_malloc0 (sizeof (guchar) * stride * height); - - glReadPixels (x, - clutter_actor_get_height (actor) - y - height, - width, - height, GL_RGBA, GL_UNSIGNED_BYTE, data); - - tmp = gdk_pixbuf_new_from_data (data, - GDK_COLORSPACE_RGB, - TRUE, - 8, - width, height, - stride, - snapshot_pixbuf_free, - NULL); - - tmp2 = gdk_pixbuf_flip (tmp, TRUE); - g_object_unref (tmp); - - pixb = gdk_pixbuf_rotate_simple (tmp2, GDK_PIXBUF_ROTATE_UPSIDEDOWN); - g_object_unref (tmp2); - } - - return pixb; -} - static void clutter_stage_glx_dispose (GObject *gobject) { @@ -424,7 +343,5 @@ clutter_stage_glx_init (ClutterStageGLX *stage) static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface) { - iface->draw_to_pixbuf = clutter_stage_glx_draw_to_pixbuf; - /* the rest is inherited from ClutterStageX11 */ } diff --git a/clutter/osx/Makefile.am b/clutter/osx/Makefile.am index c0714db27..018bd9b6e 100644 --- a/clutter/osx/Makefile.am +++ b/clutter/osx/Makefile.am @@ -4,8 +4,8 @@ libclutterinclude_HEADERS = clutter-osx.h INCLUDES = \ -DG_LOG_DOMAIN=\"ClutterOSX\" \ -I$(top_srcdir) \ - -I$(top_srcdir)/clutter/cogl \ - -I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ \ + -I$(top_srcdir)/clutter \ + -I$(top_builddir)/clutter \ $(CLUTTER_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) \ diff --git a/clutter/pango/Makefile.am b/clutter/pango/Makefile.am index bc3fc337d..41439d2c1 100644 --- a/clutter/pango/Makefile.am +++ b/clutter/pango/Makefile.am @@ -16,8 +16,8 @@ INCLUDES = \ @GCC_FLAGS@ @CLUTTER_CFLAGS@ \ $(CLUTTER_DEBUG_CFLAGS) \ -I$(top_srcdir) \ - -I$(top_srcdir)/clutter/cogl \ - -I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ + -I$(top_srcdir)/clutter \ + -I$(top_builddir)/clutter pangoclutterheadersdir = $(includedir)/clutter-@CLUTTER_MAJORMINOR@/clutter pangoclutterheaders_HEADERS = $(source_h) diff --git a/clutter/pango/pangoclutter-render.c b/clutter/pango/pangoclutter-render.c index 11ac0829f..6e3df481c 100644 --- a/clutter/pango/pangoclutter-render.c +++ b/clutter/pango/pangoclutter-render.c @@ -32,7 +32,7 @@ #include "pangoclutter-private.h" #include "../clutter-debug.h" -#include "cogl.h" +#include "cogl/cogl.h" /* * Texture cache support code @@ -43,18 +43,18 @@ #define TC_ROUND 4 typedef struct { - guint name; + CoglHandle cogl_tex; int x, y, w, h; } tc_area; typedef struct tc_texture { struct tc_texture *next; - COGLuint name; + CoglHandle cogl_tex; int avail; } tc_texture; typedef struct tc_slice { - guint name; + CoglHandle cogl_tex; int avail, y; } tc_slice; @@ -68,12 +68,12 @@ tc_clear () int i; for (i = TC_HEIGHT / TC_ROUND; i--; ) - slices [i].name = 0; + slices [i].cogl_tex = COGL_INVALID_HANDLE; while (first_texture) { tc_texture *next = first_texture->next; - cogl_textures_destroy (1, &first_texture->name); + cogl_texture_unref (first_texture->cogl_tex); g_slice_free (tc_texture, first_texture); first_texture = next; } @@ -101,7 +101,7 @@ tc_get (tc_area *area, int width, int height) width = MIN (width, TC_WIDTH); - if (!slice->name || slice->avail < width) + if (slice->cogl_tex == COGL_INVALID_HANDLE || slice->avail < width) { /* try to find a texture with enough space */ tc_texture *tex, *match = 0; @@ -113,54 +113,37 @@ tc_get (tc_area *area, int width, int height) /* create a new texture if necessary */ if (!match) { - COGLenum texture_target_type = CGL_TEXTURE_2D; - CLUTTER_NOTE (PANGO, "creating new texture %i x %i", TC_WIDTH, TC_HEIGHT); - /* Use NPOTS if available as it simply makes shaders easier to - * work with on text. - */ - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE)) - texture_target_type = CGL_TEXTURE_RECTANGLE_ARB; - match = g_slice_new (tc_texture); match->next = first_texture; first_texture = match; match->avail = TC_HEIGHT; - cogl_textures_create (1, &match->name); - - cogl_texture_bind (texture_target_type, match->name); - - /* We might even want to use mipmapping instead of CGL_LINEAR here - * that should allow rerendering of glyphs to look nice even at scales - * far below 50%. - */ - cogl_texture_set_filters (texture_target_type, + match->cogl_tex = cogl_texture_new_with_size (TC_WIDTH, TC_HEIGHT, 0, + COGL_PIXEL_FORMAT_A_8); + + /* We might even want to use mipmapping instead of + * CGL_LINEAR here that should allow rerendering of glyphs + * to look nice even at scales far below 50%. + */ + cogl_texture_set_filters (match->cogl_tex, CGL_LINEAR, CGL_NEAREST); - - cogl_texture_image_2d (texture_target_type, - CGL_ALPHA, - TC_WIDTH, - TC_HEIGHT, - CGL_ALPHA, - CGL_UNSIGNED_BYTE, - NULL); } match->avail -= slice_height; - slice->name = match->name; - slice->avail = TC_WIDTH; - slice->y = match->avail; + slice->cogl_tex = match->cogl_tex; + slice->avail = TC_WIDTH; + slice->y = match->avail; } slice->avail -= width; - area->name = slice->name; - area->x = slice->avail; - area->y = slice->y; + area->cogl_tex = slice->cogl_tex; + area->x = slice->avail; + area->y = slice->y; } static void @@ -194,8 +177,6 @@ struct _PangoClutterRenderer PangoRenderer parent_instance; ClutterColor color; int flags; - guint curtex; /* current texture */ - COGLenum texture_target_type; /* 2D or Rect */ }; G_DEFINE_TYPE (PangoClutterRenderer, \ @@ -330,9 +311,9 @@ draw_glyph (PangoRenderer *renderer_, double xd, double yd) { - PangoClutterRenderer *renderer = PANGO_CLUTTER_RENDERER (renderer_); glyph_info *g; gint x = (gint)xd, y = (gint)yd; + ClutterFixed fx, fy; if (glyph & PANGO_GLYPH_UNKNOWN_FLAG) { @@ -384,103 +365,36 @@ draw_glyph (PangoRenderer *renderer_, CLUTTER_NOTE (PANGO, "cache fail; subimage2d %i", glyph); - cogl_texture_bind (renderer->texture_target_type, g->tex.name); - - cogl_texture_set_alignment (renderer->texture_target_type, - 1, bm.stride); - - cogl_texture_sub_image_2d (renderer->texture_target_type, - g->tex.x, - g->tex.y, - bm.width, - bm.height, - CGL_ALPHA, - CGL_UNSIGNED_BYTE, - bm.bitmap); - - glTexParameteri (renderer->texture_target_type, - GL_GENERATE_MIPMAP, FALSE); - - renderer->curtex = g->tex.name; + cogl_texture_set_region (g->tex.cogl_tex, + 0, 0, + g->tex.x, g->tex.y, + bm.width, bm.height, + bm.width, bm.height, + COGL_PIXEL_FORMAT_A_8, + bm.stride, + bm.bitmap); } - else CLUTTER_NOTE (PANGO, "cache succsess %i\n", glyph); + else + CLUTTER_NOTE (PANGO, "cache success %i\n", glyph); x += g->left; y -= g->top; - if (g->tex.name != renderer->curtex) - { - cogl_texture_bind (renderer->texture_target_type, g->tex.name); - renderer->curtex = g->tex.name; - } + fx = CLUTTER_INT_TO_FIXED (g->tex.x) / TC_WIDTH; + fy = CLUTTER_INT_TO_FIXED (g->tex.y) / TC_HEIGHT; - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE)) - { - cogl_texture_quad (x, - x + g->tex.w, - y, - y + g->tex.h, - CLUTTER_INT_TO_FIXED (g->tex.x), - CLUTTER_INT_TO_FIXED (g->tex.y), - CLUTTER_INT_TO_FIXED (g->tex.w + g->tex.x), - CLUTTER_INT_TO_FIXED (g->tex.h + g->tex.y)); - } - else - { - ClutterFixed fx, fy; - - fx = CLUTTER_INT_TO_FIXED (g->tex.x) / TC_WIDTH; - fy = CLUTTER_INT_TO_FIXED (g->tex.y) / TC_HEIGHT; - - cogl_texture_quad (x, - x + g->tex.w, - y, - y + g->tex.h, - fx, - fy, - CLUTTER_INT_TO_FIXED (g->tex.w) / TC_WIDTH + fx, - CLUTTER_INT_TO_FIXED (g->tex.h) / TC_HEIGHT + fy); - } + cogl_texture_rectangle (g->tex.cogl_tex, + CLUTTER_INT_TO_FIXED (x), + CLUTTER_INT_TO_FIXED (y), + CLUTTER_INT_TO_FIXED (x + g->tex.w), + CLUTTER_INT_TO_FIXED (y + g->tex.h), + fx, fy, + CLUTTER_INT_TO_FIXED (g->tex.w) / TC_WIDTH + fx, + CLUTTER_INT_TO_FIXED (g->tex.h) / TC_WIDTH + fy); } -static void -draw_trapezoid (PangoRenderer *renderer_, - PangoRenderPart part, - double y01, - double x11, - double x21, - double y02, - double x12, - double x22) -{ - PangoClutterRenderer *renderer = (PangoClutterRenderer *)renderer_; - - if (renderer->curtex) - { - /* glEnd (); */ - renderer->curtex = 0; - } - - /* Turn texturing off */ - cogl_enable (CGL_ENABLE_BLEND); - - cogl_trapezoid ((gint) y01, - (gint) x11, - (gint) x21, - (gint) y02, - (gint) x12, - (gint) x22); - - /* Turn it back on again */ - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE)) - cogl_enable (CGL_ENABLE_TEXTURE_RECT|CGL_ENABLE_BLEND); - else - cogl_enable (CGL_ENABLE_TEXTURE_2D|CGL_ENABLE_BLEND); -} - - void -pango_clutter_render_layout_subpixel (PangoLayout *layout, +pango_clutter_render_layout_subpixel (PangoLayout *layout, int x, int y, ClutterColor *color, @@ -545,11 +459,6 @@ pango_clutter_render_clear_caches (void) static void pango_clutter_renderer_init (PangoClutterRenderer *renderer) { - renderer->texture_target_type = CGL_TEXTURE_2D; - - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE)) - renderer->texture_target_type = CGL_TEXTURE_RECTANGLE_ARB; - memset (&renderer->color, 0xff, sizeof(ClutterColor)); } @@ -614,25 +523,11 @@ prepare_run (PangoRenderer *renderer, PangoLayoutRun *run) static void draw_begin (PangoRenderer *renderer_) { - PangoClutterRenderer *renderer = (PangoClutterRenderer *)renderer_; - - renderer->curtex = 0; - - if (clutter_feature_available (CLUTTER_FEATURE_TEXTURE_RECTANGLE)) - cogl_enable (CGL_ENABLE_TEXTURE_RECT|CGL_ENABLE_BLEND); - else - cogl_enable (CGL_ENABLE_TEXTURE_2D|CGL_ENABLE_BLEND); } static void draw_end (PangoRenderer *renderer_) { - /* - PangoClutterRenderer *renderer = (PangoClutterRenderer *)renderer_; - - if (renderer->curtex) - glEnd (); - */ } static void @@ -641,7 +536,6 @@ pango_clutter_renderer_class_init (PangoClutterRendererClass *klass) PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass); renderer_class->draw_glyph = draw_glyph; - renderer_class->draw_trapezoid = draw_trapezoid; renderer_class->prepare_run = prepare_run; renderer_class->begin = draw_begin; renderer_class->end = draw_end; diff --git a/clutter/sdl/Makefile.am b/clutter/sdl/Makefile.am index 9dc13ff8e..fb944c568 100644 --- a/clutter/sdl/Makefile.am +++ b/clutter/sdl/Makefile.am @@ -4,8 +4,8 @@ libclutterinclude_HEADERS = clutter-sdl.h INCLUDES = \ -DG_LOG_DOMAIN=\"ClutterSDL\" \ -I$(top_srcdir) \ - -I$(top_srcdir)/clutter/cogl \ - -I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ \ + -I$(top_srcdir)/clutter/ \ + -I$(top_builddir)/clutter/ \ $(CLUTTER_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) diff --git a/clutter/sdl/clutter-stage-sdl.c b/clutter/sdl/clutter-stage-sdl.c index 5d867d867..d0598b050 100644 --- a/clutter/sdl/clutter-stage-sdl.c +++ b/clutter/sdl/clutter-stage-sdl.c @@ -16,7 +16,7 @@ #include "../clutter-units.h" #include "../clutter-stage-window.h" -#include "cogl.h" +#include "cogl/cogl.h" static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); diff --git a/clutter/win32/Makefile.am b/clutter/win32/Makefile.am index 818ca8362..43f665c07 100644 --- a/clutter/win32/Makefile.am +++ b/clutter/win32/Makefile.am @@ -10,8 +10,8 @@ pkgconfigdir = $(libdir)/pkgconfig INCLUDES = \ -DG_LOG_DOMAIN=\"ClutterWin32\" \ -I$(top_srcdir) \ - -I$(top_srcdir)/clutter/cogl \ - -I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ \ + -I$(top_srcdir)/clutter \ + -I$(top_builddir)/clutter \ $(CLUTTER_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) diff --git a/clutter/win32/clutter-backend-win32.c b/clutter/win32/clutter-backend-win32.c index bc86a3035..2cf3b0c5a 100644 --- a/clutter/win32/clutter-backend-win32.c +++ b/clutter/win32/clutter-backend-win32.c @@ -35,7 +35,7 @@ #include "../clutter-private.h" #include "../clutter-version.h" -#include "cogl.h" +#include "cogl/cogl.h" G_DEFINE_TYPE (ClutterBackendWin32, clutter_backend_win32, CLUTTER_TYPE_BACKEND); diff --git a/clutter/win32/clutter-stage-win32.c b/clutter/win32/clutter-stage-win32.c index 5d615f50c..c4c1c483c 100644 --- a/clutter/win32/clutter-stage-win32.c +++ b/clutter/win32/clutter-stage-win32.c @@ -40,7 +40,7 @@ #include "../clutter-shader.h" #include "../clutter-stage.h" -#include "cogl.h" +#include "cogl/cogl.h" #include diff --git a/clutter/x11/Makefile.am b/clutter/x11/Makefile.am index d1ab34bf0..d3632d7d1 100644 --- a/clutter/x11/Makefile.am +++ b/clutter/x11/Makefile.am @@ -11,8 +11,8 @@ pkgconfigdir = $(libdir)/pkgconfig INCLUDES = \ -DG_LOG_DOMAIN=\"ClutterX11\" \ -I$(top_srcdir) \ - -I$(top_srcdir)/clutter/cogl \ - -I$(top_srcdir)/clutter/cogl/@CLUTTER_COGL@ \ + -I$(top_srcdir)/clutter \ + -I$(top_builddir)/clutter \ $(CLUTTER_CFLAGS) \ $(CLUTTER_DEBUG_CFLAGS) \ $(GCC_FLAGS) diff --git a/clutter/x11/clutter-backend-x11.c b/clutter/x11/clutter-backend-x11.c index 1fbf0e72e..1200e24e5 100644 --- a/clutter/x11/clutter-backend-x11.c +++ b/clutter/x11/clutter-backend-x11.c @@ -43,7 +43,7 @@ #include "../clutter-debug.h" #include "../clutter-private.h" -#include "cogl.h" +#include "cogl/cogl.h" G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND); diff --git a/clutter/x11/clutter-stage-x11.c b/clutter/x11/clutter-stage-x11.c index 8076e71f5..a3d99e6b4 100644 --- a/clutter/x11/clutter-stage-x11.c +++ b/clutter/x11/clutter-stage-x11.c @@ -38,14 +38,12 @@ #include "../clutter-debug.h" #include "../clutter-units.h" -#include "cogl.h" +#include "cogl/cogl.h" #ifdef HAVE_XFIXES #include #endif -#include - static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterStageX11, diff --git a/clutter/x11/clutter-x11-texture-pixmap.c b/clutter/x11/clutter-x11-texture-pixmap.c index 32685401f..5dc29cb87 100644 --- a/clutter/x11/clutter-x11-texture-pixmap.c +++ b/clutter/x11/clutter-x11-texture-pixmap.c @@ -45,7 +45,7 @@ #include "clutter-x11.h" #include "clutter-backend-x11.h" -#include "cogl.h" +#include "cogl/cogl.h" /* FIXME: Check exts exist in autogen */ #include @@ -454,6 +454,9 @@ clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture, priv = texture->priv; dpy = clutter_x11_get_default_display(); + if (!priv->pixmap) + return; + clutter_x11_trap_x_errors (); /* FIXME: Use XSHM here! */ diff --git a/configure.ac b/configure.ac index f4aed2727..9611f3c1e 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,8 @@ dnl ======================================================================== # Checks for programs. AC_PROG_CC +#_AM_DEPENDENCIES([OBJC]) +#AC_PROG_OBJC AC_DISABLE_STATIC AC_PROG_LIBTOOL @@ -76,6 +78,26 @@ AC_FUNC_MALLOC AC_FUNC_MMAP AC_CHECK_FUNCS([memset munmap strcasecmp strdup]) +dnl ======================================================================== + +imagebackend="gdk-pixbuf" +AC_ARG_WITH([imagebackend], + AC_HELP_STRING([--with-imagebackend=@<:@gdk-pixbuf/internal@:>@], + [Select COGL image loading backend]), + imagebackend=$with_imagebackend) + +case $imagebackend in + + gdk-pixbuf) + AC_DEFINE([USE_GDKPIXBUF], 1, [Use GdkPixbuf for loading image files]) + ;; + + internal) + ;; + + +esac + dnl ======================================================================== @@ -127,6 +149,8 @@ if test x$have_xcomposite = xyes; then X11_LIBS="$X11_LIBS -lXcomposite" fi +x11_tests=no + # Currently require all extentions, may not for actual release. if test "x$clutterbackend" = "xglx" || test "x$clutterbackend" = "xeglx" then @@ -134,7 +158,10 @@ then then AC_MSG_ERROR([[Required backend X11 Libraries not found.]]) fi -fi + x11_tests=yes +fi + +AM_CONDITIONAL(X11_TESTS, test "x$x11_tests" != "xno") clutter_gl_header="" @@ -203,8 +230,10 @@ case $clutterbackend in AC_MSG_ERROR([GLX not found and GLX backend requested]); fi - # xlib pixbuf needed for exporting the stage - BACKEND_PC_FILES="gdk-pixbuf-xlib-2.0" + if test "x$imagebackend" = "xgdk-pixbuf"; then + # xlib pixbuf needed for exporting the stage + BACKEND_PC_FILES="gdk-pixbuf-xlib-2.0" + fi GLX_LIBS="$X11_LIBS -lGL" GLX_CFLAGS="$X11_CFLAGS" @@ -286,6 +315,35 @@ case $clutterbackend in EGL_CFLAGS="$TSLIB_CFLAGS" ;; + fruity) + + clutter_gl_header="GLES/gl.h" + + + CLUTTER_FLAVOUR="fruity" + CLUTTER_STAGE_TYPE="CLUTTER_TYPE_STAGE_FRUITY" + AC_DEFINE([HAVE_CLUTTER_FRUITY], 1, [We're building a fruity version of the eglnative backend]) + + # We currently assume having egl means also having gles.. + CLUTTER_COGL="gles" + + AC_DEFINE([HAVE_CLUTTER_EGL], 1, [Have the EGL backend]) + + # We currently assume having egl means also having gles.. + CLUTTER_COGL="gles" + AC_DEFINE([HAVE_COGL_GLES], 1, [Have GL/ES for rendering]) + + AC_CHECK_HEADERS([$clutter_gl_header],, + [AC_MSG_ERROR([Unable to locate require GL header])]) + AC_CHECK_HEADERS([GLES/egl.h],, + [AC_MSG_ERROR([Unable to locate required GLES headers])]) + + backendextra= + + EGL_LIBS="-ObjC -framework Foundation -framework CoreFoundation -framework CoreGraphics -framework CoreSurface -framework GraphicsServices -framework OpenGLES -framework LayerKit -framework UIKit" + EGL_CFLAGS="" + ;; + osx) clutter_gl_header="OpenGL/gl.h" @@ -325,7 +383,7 @@ case $clutterbackend in CLUTTER_LT_LDFLAGS="$CLUTTER_LT_LDFLAGS -no-undefined" ;; - *) AC_MSG_ERROR([Invalid backend for Clutter: use glx,sdl,osx,win32,eglx or eglnative]) + *) AC_MSG_ERROR([Invalid backend for Clutter: use glx,sdl,osx,win32,eglx, eglnative or fruity]) ;; esac @@ -333,6 +391,7 @@ esac CLUTTER_GL_HEADER=$clutter_gl_header AC_SUBST([clutterbackend]) +AC_SUBST([imagebackend]) AC_SUBST([backendextra]) AC_SUBST([backendextralib]) AC_SUBST(CLUTTER_FLAVOUR) @@ -381,7 +440,12 @@ fi AC_SUBST(JSON_PREFIX) AM_CONDITIONAL(LOCAL_JSON_GLIB, test "x$have_json" = "xno") -CLUTTER_REQUIRES="pangoft2 glib-2.0 >= 2.14 gobject-2.0 gthread-2.0 gdk-pixbuf-2.0 $BACKEND_PC_FILES $JSON_GLIB_PC" +CLUTTER_REQUIRES="pangoft2 glib-2.0 >= 2.14 gobject-2.0 gthread-2.0 gmodule-2.0 $BACKEND_PC_FILES $JSON_GLIB_PC" + +if test "x$imagebackend" = "xgdk-pixbuf"; then + CLUTTER_REQUIRES="$CLUTTER_REQUIRES gdk-pixbuf-2.0" +fi + PKG_CHECK_MODULES(CLUTTER_DEPS, [$CLUTTER_REQUIRES]) AC_SUBST(CLUTTER_REQUIRES) @@ -485,20 +549,26 @@ AC_CONFIG_FILES([ clutter/glx/Makefile clutter/eglx/Makefile clutter/eglnative/Makefile + clutter/fruity/Makefile clutter/osx/Makefile clutter/win32/Makefile clutter/win32/clutter-win32.pc clutter/sdl/Makefile clutter/cogl/Makefile + clutter/cogl/common/Makefile + clutter/cogl/cogl.h + clutter/cogl/cogl-defines-gl.h:clutter/cogl/gl/cogl-defines.h.in + clutter/cogl/cogl-defines-gles.h:clutter/cogl/gles/cogl-defines.h.in clutter/cogl/gl/Makefile - clutter/cogl/gl/cogl-defines.h clutter/cogl/gles/Makefile clutter/json/Makefile clutter/pango/Makefile tests/Makefile doc/Makefile doc/reference/Makefile - doc/reference/version.xml + doc/reference/clutter/Makefile + doc/reference/clutter/version.xml + doc/reference/cogl/Makefile doc/manual/clutter-manual.xml doc/manual/Makefile clutter.pc @@ -522,3 +592,29 @@ echo " Compiler flags: ${CPPFLAGS}" echo " API Documentation: ${enable_gtk_doc}" echo " Manual Documentation: ${enable_manual}" echo "" + + +# you can add more configuration options or flags that will bring up the +# the experimental/unstable warning summary. + +if test "x$imagebackend" = "xinternal"; then + experimental_stuff="$experimental_stuff\n imagebackend=$imagebackend" +fi +if test "x$clutterbackend" = "xfruity"; then + experimental_stuff="$experimental_stuff\n clutterbackend=$clutterbackend" +fi + + +if test "x$experimental_stuff" != "x"; then +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +echo "" +echo "Experimental features configured, stability of your build either uses" +echo "experimental backends or experimental and unsupported features:" +echo -e "$experimental_stuff" +echo "" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" +fi + + diff --git a/doc/reference/ChangeLog b/doc/reference/ChangeLog index 1b9ce74b7..79c950eae 100644 --- a/doc/reference/ChangeLog +++ b/doc/reference/ChangeLog @@ -1,3 +1,50 @@ +2008-04-22 Øyvind Kolås + + * clutter/Makefile.am: + * cogl/Makefile.am: s/EXTRA_DIST +=/EXTRA_DIST =/ since newer automake + is more stringent and EXTRA_DIST has not been defined earlier. + +2008-04-21 Neil Roberts + + * cogl/cogl-sections.txt: Added CoglTextureVertex, + cogl_texture_can_polygon and cogl_texture_polygon. + +2008-04-18 Emmanuele Bassi + + * cogl/cogl-docs.sgml: Fill out and add an "about" section. + + * cogl/cogl-sections.txt: Remove unused/redundant stuff, + divide into logical subsections and in general make the + documentation more structured. + + * cogl/Makefile.am: Ignore some private header files. + +2008-04-18 Emmanuele Bassi + + * cogl/: Add COGL documentation. + + * Makefile.am: Add cogl/ to the list of SUBDIRS. + +2008-04-18 Emmanuele Bassi + + * clutter/: + * *: Moved everything into the clutter/ subdirectory, to make + room for the COGL API reference. + +2008-04-07 Neil Roberts + + * clutter-sections.txt: Removed clutter_texture_new_from_pixbuf + and clutter_texture_{get,set}_pixbuf. Added + clutter_texture_{set,new}_from_file. + +2008-04-03 Neil Roberts + + * clutter-sections.txt: Removed ClutterTexture functions that are + no longer neccessary to implement ClutterCloneTexture because of + the new COGL texture API. Added + clutter_texture_{get,set}_filter_quality and + clutter_texture_get_cogl_texture. + 2008-03-30 Neil Roberts * clutter-sections.txt: Added clutter_win32_get_stage_from_window diff --git a/doc/reference/Makefile.am b/doc/reference/Makefile.am index e9b5d659f..c136c4721 100644 --- a/doc/reference/Makefile.am +++ b/doc/reference/Makefile.am @@ -1,129 +1 @@ -## Process this file with automake to produce Makefile.in - -# We require automake 1.6 at least. -AUTOMAKE_OPTIONS = 1.6 - -# This is a blank Makefile.am for using gtk-doc. -# Copy this to your project's API docs directory and modify the variables to -# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples -# of using the various options. - -# The name of the module, e.g. 'glib'. -DOC_MODULE=clutter - -# The top-level SGML file. You can change this if you want to. -DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml - -# The directory containing the source code. Relative to $(srcdir). -# gtk-doc will search all .c & .h files beneath here for inline comments -# documenting the functions and macros. -# e.g. DOC_SOURCE_DIR=../../../gtk -DOC_SOURCE_DIR=../../clutter - -# Extra options to pass to gtkdoc-scangobj. Not normally needed. -SCANGOBJ_OPTIONS=--type-init-func="clutter_base_init()" - -# Extra options to supply to gtkdoc-scan. -# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" -SCAN_OPTIONS=--deprecated-guards="CLUTTER_DISABLE_DEPRECATED" - -# Extra options to supply to gtkdoc-mkdb. -# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml -MKDB_OPTIONS=--sgml-mode --output-format=xml - -# Extra options to supply to gtkdoc-mktmpl -# e.g. MKTMPL_OPTIONS=--only-section-tmpl -MKTMPL_OPTIONS= - -# Extra options to supply to gtkdoc-fixref. Not normally needed. -# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html -FIXXREF_OPTIONS=\ - --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/gobject \ - --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/glib \ - --extra-dir=$(PANGO_PREFIX)/share/gtk-doc/html/pango \ - --extra-dir=$(GDPIXBUF_PREFIX)/share/gtk-doc/html/gdk-pixbuf - -# Used for dependencies. The docs will be rebuilt if any of these change. -# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h -# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c -HFILE_GLOB=$(top_srcdir)/clutter/*.h \ - $(top_srcdir)/clutter/x11/clutter-x11.h \ - $(top_srcdir)/clutter/win32/clutter-win32.h -CFILE_GLOB=$(top_srcdir)/clutter/*.c - -# Header files to ignore when scanning. -# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h -IGNORE_HFILES=\ - clutter.h \ - clutter-debug.h \ - clutter-deprecated.h \ - clutter-enum-types.h \ - clutter-json.h \ - clutter-keysyms.h \ - clutter-keysyms-table.h \ - clutter-marshal.h \ - clutter-model-private.h \ - clutter-private.h \ - clutter-id-pool.h \ - clutter-script-private.h \ - stamp-clutter-enum-types.h \ - stamp-clutter-marshal.h \ - cogl \ - eglnative \ - eglx \ - glx \ - osx \ - x11 \ - json \ - pango \ - sdl \ - win32 - -EXTRA_HFILES=\ - ../../clutter/x11/clutter-x11.h \ - ../../clutter/win32/clutter-win32.h - -# Images to copy into HTML directory. -# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png -HTML_IMAGES=\ - actor-box.png \ - alpha-func.png \ - event-flow.png \ - path-alpha-func.png - -# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). -# e.g. content_files=running.sgml building.sgml changes-2.0.sgml -content_files= \ - subclassing-ClutterActor.sgml \ - clutter-animation.sgml \ - creating-your-own-behaviours.sgml \ - version.xml - -# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded -# These files must be listed here *and* in content_files -# e.g. expand_content_files=running.sgml -expand_content_files= \ - subclassing-ClutterActor.sgml \ - clutter-animation.sgml \ - creating-your-own-behaviours.sgml - -# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. -# Only needed if you are using gtkdoc-scangobj to dynamically query widget -# signals and properties. -# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) -# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) - -INCLUDES=-I$(top_srcdir) $(CLUTTER_CFLAGS) -GTKDOC_LIBS=$(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la $(CLUTTER_LIBS) - -# This includes the standard gtk-doc make rules, copied by gtkdocize. -include $(top_srcdir)/gtk-doc.make - -# Other files to distribute -# e.g. EXTRA_DIST += version.xml.in -EXTRA_DIST += \ - version.xml.in \ - actor-box.png \ - alpha-func.png \ - event-flow.png \ - path-alpha-func.png +SUBDIRS = clutter cogl diff --git a/doc/reference/clutter/Makefile.am b/doc/reference/clutter/Makefile.am new file mode 100644 index 000000000..a956edcaa --- /dev/null +++ b/doc/reference/clutter/Makefile.am @@ -0,0 +1,129 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=clutter + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=../../../clutter + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS=--type-init-func="clutter_base_init()" + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS=--deprecated-guards="CLUTTER_DISABLE_DEPRECATED" + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS=\ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/gobject \ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/glib \ + --extra-dir=$(PANGO_PREFIX)/share/gtk-doc/html/pango \ + --extra-dir=$(GDPIXBUF_PREFIX)/share/gtk-doc/html/gdk-pixbuf + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=$(top_srcdir)/clutter/*.h \ + $(top_srcdir)/clutter/x11/clutter-x11.h \ + $(top_srcdir)/clutter/win32/clutter-win32.h +CFILE_GLOB=$(top_srcdir)/clutter/*.c + +# Header files to ignore when scanning. +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES=\ + clutter.h \ + clutter-debug.h \ + clutter-deprecated.h \ + clutter-enum-types.h \ + clutter-json.h \ + clutter-keysyms.h \ + clutter-keysyms-table.h \ + clutter-marshal.h \ + clutter-model-private.h \ + clutter-private.h \ + clutter-id-pool.h \ + clutter-script-private.h \ + stamp-clutter-enum-types.h \ + stamp-clutter-marshal.h \ + cogl \ + eglnative \ + eglx \ + glx \ + osx \ + x11 \ + json \ + pango \ + sdl \ + win32 + +EXTRA_HFILES=\ + ../../../clutter/x11/clutter-x11.h \ + ../../../clutter/win32/clutter-win32.h + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES=\ + actor-box.png \ + alpha-func.png \ + event-flow.png \ + path-alpha-func.png + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files= \ + subclassing-ClutterActor.sgml \ + clutter-animation.sgml \ + creating-your-own-behaviours.sgml \ + version.xml + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= \ + subclassing-ClutterActor.sgml \ + clutter-animation.sgml \ + creating-your-own-behaviours.sgml + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) + +INCLUDES=-I$(top_srcdir) -I$(top_srcdir)/clutter $(CLUTTER_CFLAGS) +GTKDOC_LIBS=$(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la $(CLUTTER_LIBS) + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST = \ + version.xml.in \ + actor-box.png \ + alpha-func.png \ + event-flow.png \ + path-alpha-func.png diff --git a/doc/reference/actor-box.png b/doc/reference/clutter/actor-box.png similarity index 100% rename from doc/reference/actor-box.png rename to doc/reference/clutter/actor-box.png diff --git a/doc/reference/alpha-func.png b/doc/reference/clutter/alpha-func.png similarity index 100% rename from doc/reference/alpha-func.png rename to doc/reference/clutter/alpha-func.png diff --git a/doc/reference/clutter-animation.sgml b/doc/reference/clutter/clutter-animation.sgml similarity index 100% rename from doc/reference/clutter-animation.sgml rename to doc/reference/clutter/clutter-animation.sgml diff --git a/doc/reference/clutter-docs.sgml b/doc/reference/clutter/clutter-docs.sgml similarity index 100% rename from doc/reference/clutter-docs.sgml rename to doc/reference/clutter/clutter-docs.sgml diff --git a/doc/reference/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt similarity index 96% rename from doc/reference/clutter-sections.txt rename to doc/reference/clutter/clutter-sections.txt index 94ca6dd10..1f382ea62 100644 --- a/doc/reference/clutter-sections.txt +++ b/doc/reference/clutter/clutter-sections.txt @@ -28,15 +28,14 @@ clutter_media_get_type clutter-units Unit conversion ClutterUnit +CLUTTER_UNITS_FROM_DEVICE +CLUTTER_UNITS_TO_DEVICE CLUTTER_UNITS_FROM_FIXED CLUTTER_UNITS_TO_FIXED CLUTTER_UNITS_FROM_FLOAT CLUTTER_UNITS_TO_FLOAT CLUTTER_UNITS_FROM_INT CLUTTER_UNITS_TO_INT - -CLUTTER_UNITS_FROM_DEVICE -CLUTTER_UNITS_TO_DEVICE CLUTTER_UNITS_FROM_PANGO_UNIT CLUTTER_UNITS_TO_PANGO_UNIT CLUTTER_UNITS_TMP_FROM_DEVICE @@ -49,23 +48,6 @@ CLUTTER_UNITS_FROM_MM CLUTTER_UNITS_FROM_MMX CLUTTER_UNITS_FROM_POINTS CLUTTER_UNITS_FROM_POINTSX - -CLUTTER_VALUE_HOLDS_UNIT -clutter_value_set_unit -clutter_value_get_unit - -CLUTTER_MAXUNIT -CLUTTER_MINUNIT -ClutterParamSpecUnit -clutter_param_spec_unit - -CLUTTER_TYPE_UNIT -CLUTTER_TYPE_PARAM_UNIT -CLUTTER_PARAM_SPEC_UNIT -CLUTTER_IS_PARAM_SPEC_UNIT - -clutter_unit_get_type -clutter_param_unit_get_type
@@ -446,24 +428,21 @@ ClutterTexture ClutterTextureClass ClutterTextureFlags clutter_texture_new -clutter_texture_new_from_pixbuf +clutter_texture_new_from_file clutter_texture_new_from_actor CLUTTER_TEXTURE_ERROR ClutterTextureError +clutter_texture_set_from_file clutter_texture_set_from_rgb_data clutter_texture_set_from_yuv_data clutter_texture_set_area_from_rgb_data -clutter_texture_set_pixbuf -clutter_texture_get_pixbuf clutter_texture_get_base_size - - -clutter_texture_bind_tile -clutter_texture_get_n_tiles -clutter_texture_get_x_tile_detail -clutter_texture_get_y_tile_detail -clutter_texture_has_generated_tiles -clutter_texture_is_tiled +clutter_texture_get_filter_quality +clutter_texture_set_filter_quality +clutter_texture_get_max_tile_waste +clutter_texture_set_max_tile_waste +clutter_texture_get_cogl_texture +clutter_texture_set_cogl_texture CLUTTER_TEXTURE @@ -874,31 +853,12 @@ clutter_qmulx clutter_qdivx clutter_tani - -CLUTTER_VALUE_HOLDS_FIXED -clutter_value_set_fixed -clutter_value_get_fixed - - -CLUTTER_MAXFIXED -CLUTTER_MINFIXED -ClutterParamSpecFixed -clutter_param_spec_fixed - - -CLUTTER_PARAM_SPEC_FIXED -CLUTTER_IS_PARAM_SPEC_FIXED - CFX_DIV CFX_INT CFX_MUL CFX_QMUL CFX_QDIV -CLUTTER_TYPE_FIXED -CLUTTER_TYPE_PARAM_FIXED -clutter_fixed_get_type -clutter_param_fixed_get_type
diff --git a/doc/reference/clutter.types b/doc/reference/clutter/clutter.types similarity index 100% rename from doc/reference/clutter.types rename to doc/reference/clutter/clutter.types diff --git a/doc/reference/creating-your-own-behaviours.sgml b/doc/reference/clutter/creating-your-own-behaviours.sgml similarity index 100% rename from doc/reference/creating-your-own-behaviours.sgml rename to doc/reference/clutter/creating-your-own-behaviours.sgml diff --git a/doc/reference/event-flow.png b/doc/reference/clutter/event-flow.png similarity index 100% rename from doc/reference/event-flow.png rename to doc/reference/clutter/event-flow.png diff --git a/doc/reference/path-alpha-func.png b/doc/reference/clutter/path-alpha-func.png similarity index 100% rename from doc/reference/path-alpha-func.png rename to doc/reference/clutter/path-alpha-func.png diff --git a/doc/reference/subclassing-ClutterActor.sgml b/doc/reference/clutter/subclassing-ClutterActor.sgml similarity index 100% rename from doc/reference/subclassing-ClutterActor.sgml rename to doc/reference/clutter/subclassing-ClutterActor.sgml diff --git a/doc/reference/version.xml.in b/doc/reference/clutter/version.xml.in similarity index 100% rename from doc/reference/version.xml.in rename to doc/reference/clutter/version.xml.in diff --git a/doc/reference/cogl/Makefile.am b/doc/reference/cogl/Makefile.am new file mode 100644 index 000000000..39daa63dc --- /dev/null +++ b/doc/reference/cogl/Makefile.am @@ -0,0 +1,93 @@ +## Process this file with automake to produce Makefile.in + +# We require automake 1.6 at least. +AUTOMAKE_OPTIONS = 1.6 + +# This is a blank Makefile.am for using gtk-doc. +# Copy this to your project's API docs directory and modify the variables to +# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples +# of using the various options. + +# The name of the module, e.g. 'glib'. +DOC_MODULE=cogl + +# The top-level SGML file. You can change this if you want to. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting the functions and macros. +# e.g. DOC_SOURCE_DIR=../../../gtk +DOC_SOURCE_DIR=../../../clutter/cogl + +# Extra options to pass to gtkdoc-scangobj. Not normally needed. +SCANGOBJ_OPTIONS= + +# Extra options to supply to gtkdoc-scan. +# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED" +SCAN_OPTIONS=--deprecated-guards="CLUTTER_DISABLE_DEPRECATED" + +# Extra options to supply to gtkdoc-mkdb. +# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-mktmpl +# e.g. MKTMPL_OPTIONS=--only-section-tmpl +MKTMPL_OPTIONS= + +# Extra options to supply to gtkdoc-fixref. Not normally needed. +# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html +FIXXREF_OPTIONS=\ + --extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html/glib \ + --extra-dir=$(GDPIXBUF_PREFIX)/share/gtk-doc/html/gdk-pixbuf \ + --extra-dir=$(top_srcdir)/doc/reference/clutter/html + +# Used for dependencies. The docs will be rebuilt if any of these change. +# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h +# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c +HFILE_GLOB=\ + $(top_srcdir)/clutter/cogl/*.h \ + $(top_srcdir)/clutter/cogl/common/*.h +CFILE_GLOB=\ + $(top_srcdir)/clutter/cogl/common/*.c + +# Header files to ignore when scanning. +# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h +IGNORE_HFILES=\ + cogl-bitmap.h \ + cogl-context.h \ + cogl-defines.h \ + cogl-internal.h \ + cogl-primitives.h \ + cogl-texture.h + +EXTRA_HFILES= + +# Images to copy into HTML directory. +# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png +HTML_IMAGES= + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +# e.g. content_files=running.sgml building.sgml changes-2.0.sgml +content_files= + +# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded +# These files must be listed here *and* in content_files +# e.g. expand_content_files=running.sgml +expand_content_files= + +# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library. +# Only needed if you are using gtkdoc-scangobj to dynamically query widget +# signals and properties. +# e.g. INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) +# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) + +INCLUDES=-I$(top_srcdir) -I$(top_srcdir)/clutter $(CLUTTER_CFLAGS) +GTKDOC_LIBS=$(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la $(CLUTTER_LIBS) + +# This includes the standard gtk-doc make rules, copied by gtkdocize. +include $(top_srcdir)/gtk-doc.make + +# Other files to distribute +# e.g. EXTRA_DIST += version.xml.in +EXTRA_DIST = diff --git a/doc/reference/cogl/cogl-docs.sgml b/doc/reference/cogl/cogl-docs.sgml new file mode 100644 index 000000000..bac6e42b0 --- /dev/null +++ b/doc/reference/cogl/cogl-docs.sgml @@ -0,0 +1,57 @@ + + + + + COGL Reference Manual + + + + + 2008 + OpenedHand LTD + + + + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free + Documentation License, Version 1.1 or any later + version published by the Free Software Foundation with no + Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. You may obtain a copy of the GNU Free + Documentation License from the Free Software + Foundation by visiting their Web site or by writing + to: + +
+ The Free Software Foundation, Inc., + 59 Temple Place - Suite 330, + Boston, MA 02111-1307 +, + USA +
+
+
+ +
+ + + COGL - GL Abstraction API + +
+ About COGL + + FIXME + +
+ + + + + + + +
+
diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt new file mode 100644 index 000000000..33d49c9b3 --- /dev/null +++ b/doc/reference/cogl/cogl-sections.txt @@ -0,0 +1,148 @@ +
+cogl +General API +COGL_PIXEL_FORMAT_24 +COGL_PIXEL_FORMAT_32 +COGL_A_BIT +COGL_BGR_BIT +COGL_AFIRST_BIT +COGL_PREMULT_BIT +COGL_UNORDERED_MASK +COGL_UNPREMULT_MASK +CoglPixelFormat +CoglBufferTarget + +cogl_perspective +cogl_setup_viewport +cogl_get_modelview_matrix +cogl_get_projection_matrix +cogl_get_viewport + +cogl_push_matrix +cogl_pop_matrix +cogl_scale +cogl_translatex +cogl_translate +cogl_rotatex +cogl_rotate + +cogl_clip_set +cogl_clip_unset + +cogl_enable_depth_test +cogl_alpha_func +cogl_fog_set +
+ +
+cogl-util +Utility API +cogl_create_context +cogl_destroy_context +COGL_INVALID_HANDLE +CoglHandle +CoglFuncPtr + +CoglFeatureFlags +cogl_get_features +cogl_features_available + +cogl_get_proc_address +cogl_check_extension +cogl_get_bitmasks +cogl_paint_init + +cogl_util_next_p2 +
+ +
+cogl-primitives +Primitives +cogl_color +cogl_fast_fill_rectangle +cogl_fast_fill_rectanglex +cogl_fast_fill_trapezoid +cogl_fast_fill_trapezoidx +cogl_fill +cogl_stroke +cogl_path_move_to +cogl_path_move_to_rel +cogl_path_line_to +cogl_path_line_to_rel +cogl_path_h_line_to +cogl_path_v_line_to +cogl_path_h_line_to_rel +cogl_path_v_line_to_rel +cogl_path_arc +cogl_path_arc_rel +cogl_path_bezier2_to +cogl_path_bezier2_to_rel +cogl_path_bezier3_to +cogl_path_bezier3_to_rel +cogl_path_close +cogl_line +cogl_polyline +cogl_polygon +cogl_rectangle +cogl_arc +cogl_ellipse +cogl_round_rectangle +
+ +
+cogl-texture +Textures +CoglTextureVertex +cogl_texture_new_with_size +cogl_texture_new_from_file +cogl_texture_new_from_data +cogl_texture_new_from_foreign +cogl_is_texture +cogl_texture_get_width +cogl_texture_get_height +cogl_texture_get_format +cogl_texture_get_rowstride +cogl_texture_get_max_waste +cogl_texture_get_min_filter +cogl_texture_get_mag_filter +cogl_texture_is_sliced +cogl_texture_get_gl_texture +cogl_texture_get_data +cogl_texture_set_filters +cogl_texture_set_region +cogl_texture_ref +cogl_texture_unref +cogl_texture_rectangle +cogl_texture_polygon +
+ +
+cogl-shaders +Shaders and Programmable Pipeline +cogl_create_shader +cogl_shader_destroy +cogl_shader_source +cogl_shader_compile +cogl_shader_get_info_log +cogl_shader_get_parameteriv + +cogl_create_program +cogl_program_destroy +cogl_program_attach_shader +cogl_program_link +cogl_program_use +cogl_program_get_uniform_location +cogl_program_uniform_1f +
+ +
+cogl-offscreen +Offscreen Buffers +cogl_offscreen_new_to_texture +cogl_offscreen_new_multisample +cogl_offscreen_ref +cogl_offscreen_unref +cogl_offscreen_blit +cogl_offscreen_blit_region +cogl_draw_buffer +
diff --git a/tests/Makefile.am b/tests/Makefile.am index a14a5b1b6..b22c31ec3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -7,9 +7,17 @@ noinst_PROGRAMS = test-textures test-events test-offscreen test-scale \ test-threads test-timeline test-score test-script \ test-model test-grab test-effects test-fullscreen \ test-shader test-unproject test-viewport test-fbo \ - test-opacity test-multistage test-pixmap + test-opacity test-multistage \ + test-cogl-primitives test-cogl-tex-tile \ + test-cogl-tex-convert test-cogl-tex-foreign \ + test-cogl-tex-getset test-cogl-offscreen \ + test-cogl-tex-polygon test-stage-read-pixels -INCLUDES = -I$(top_srcdir)/ +if X11_TESTS +noinst_PROGRAMS += test-pixmap +endif + +INCLUDES = -I$(top_srcdir)/ -I$(top_builddir)/clutter LDADD = $(top_builddir)/clutter/libclutter-@CLUTTER_FLAVOUR@-@CLUTTER_MAJORMINOR@.la AM_CFLAGS = $(CLUTTER_CFLAGS) AM_LDFLAGS = $(CLUTTER_LIBS) @@ -45,5 +53,12 @@ test_fbo_SOURCES = test-fbo.c test_opacity_SOURCES = test-opacity.c test_multistage_SOURCES = test-multistage.c test_pixmap_SOURCES = test-pixmap.c +test_cogl_primitives_SOURCES = test-cogl-primitives.c +test_cogl_tex_tile_SOURCES = test-cogl-tex-tile.c +test_cogl_tex_convert_SOURCES = test-cogl-tex-convert.c +test_cogl_tex_foreign_SOURCES = test-cogl-tex-foreign.c +test_cogl_tex_getset_SOURCES = test-cogl-tex-getset.c +test_cogl_offscreen_SOURCES = test-cogl-offscreen.c +test_stage_read_pixels_SOURCES = test-stage-read-pixels.c EXTRA_DIST = redhand.png test-script.json diff --git a/tests/test-actors.c b/tests/test-actors.c index 1633a6b5d..45cc92f37 100644 --- a/tests/test-actors.c +++ b/tests/test-actors.c @@ -17,7 +17,6 @@ typedef struct SuperOH { ClutterActor **hand, *bgtex; ClutterActor *group; - GdkPixbuf *bgpixb; } SuperOH; @@ -126,7 +125,6 @@ main (int argc, char *argv[]) ClutterBehaviour *scaler_1, *scaler_2; ClutterActor *stage; ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; - GdkPixbuf *pixbuf; SuperOH *oh; gint i; GError *error; @@ -150,11 +148,6 @@ main (int argc, char *argv[]) stage = clutter_stage_get_default (); clutter_actor_set_size (stage, 800, 600); - pixbuf = gdk_pixbuf_new_from_file ("redhand.png", NULL); - - if (!pixbuf) - g_error("pixbuf load failed"); - clutter_stage_set_title (CLUTTER_STAGE (stage), "Actors Test"); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); @@ -188,9 +181,16 @@ main (int argc, char *argv[]) gint x, y, w, h; gint radius = get_radius (); - /* Create a texture from pixbuf, then clone in to same resources */ + /* Create a texture from file, then clone in to same resources */ if (i == 0) - oh->hand[i] = clutter_texture_new_from_pixbuf (pixbuf); + { + if ((oh->hand[i] = clutter_texture_new_from_file ("redhand.png", + &error)) == NULL) + { + g_error ("image load failed: %s", error->message); + exit (1); + } + } else oh->hand[i] = clutter_clone_texture_new (CLUTTER_TEXTURE(oh->hand[0])); diff --git a/tests/test-behave.c b/tests/test-behave.c index e65cbd8d7..678742641 100644 --- a/tests/test-behave.c +++ b/tests/test-behave.c @@ -78,7 +78,6 @@ main (int argc, char *argv[]) ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xff }; ClutterColor rect_bg_color = { 0x33, 0x22, 0x22, 0xff }; ClutterColor rect_border_color = { 0, 0, 0, 0 }; - GdkPixbuf *pixbuf; int i; path_t path_type = PATH_POLY; @@ -134,11 +133,6 @@ main (int argc, char *argv[]) G_CALLBACK (clutter_main_quit), NULL); - pixbuf = gdk_pixbuf_new_from_file ("redhand.png", NULL); - - if (!pixbuf) - g_error("pixbuf load failed"); - clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); @@ -147,11 +141,20 @@ main (int argc, char *argv[]) clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); clutter_actor_show (group); + hand = clutter_texture_new_from_file ("redhand.png", NULL); + if (hand == NULL) + { + g_error("pixbuf load failed"); + return 1; + } + clutter_actor_set_position (hand, 0, 0); + clutter_actor_show (hand); + rect = clutter_rectangle_new (); clutter_actor_set_position (rect, 0, 0); clutter_actor_set_size (rect, - gdk_pixbuf_get_width (pixbuf), - gdk_pixbuf_get_height (pixbuf)); + clutter_actor_get_width (hand), + clutter_actor_get_height (hand)); clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &rect_bg_color); clutter_rectangle_set_border_width (CLUTTER_RECTANGLE (rect), 10); @@ -160,10 +163,6 @@ main (int argc, char *argv[]) &rect_border_color); clutter_actor_show (rect); - hand = clutter_texture_new_from_pixbuf (pixbuf); - clutter_actor_set_position (hand, 0, 0); - clutter_actor_show (hand); - clutter_container_add (CLUTTER_CONTAINER (group), rect, hand, NULL); /* Make a timeline */ diff --git a/tests/test-cogl-offscreen.c b/tests/test-cogl-offscreen.c new file mode 100644 index 000000000..6d95db830 --- /dev/null +++ b/tests/test-cogl-offscreen.c @@ -0,0 +1,221 @@ +#include +#include +#include +#include +#include + +/* Coglbox declaration + *--------------------------------------------------*/ + +G_BEGIN_DECLS + +#define TEST_TYPE_COGLBOX test_coglbox_get_type() + +#define TEST_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_IS_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TEST_TYPE_COGLBOX)) + +#define TEST_IS_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TEST_TYPE_COGLBOX)) + +#define TEST_COGLBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +typedef struct _TestCoglbox TestCoglbox; +typedef struct _TestCoglboxClass TestCoglboxClass; +typedef struct _TestCoglboxPrivate TestCoglboxPrivate; + +struct _TestCoglbox +{ + ClutterActor parent; + + /*< private >*/ + TestCoglboxPrivate *priv; +}; + +struct _TestCoglboxClass +{ + ClutterActorClass parent_class; + + /* padding for future expansion */ + void (*_test_coglbox1) (void); + void (*_test_coglbox2) (void); + void (*_test_coglbox3) (void); + void (*_test_coglbox4) (void); +}; + +GType test_coglbox_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +/* Coglbox private declaration + *--------------------------------------------------*/ + +G_DEFINE_TYPE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); + +#define TEST_COGLBOX_GET_PRIVATE(obj) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TEST_TYPE_COGLBOX, TestCoglboxPrivate)) + +struct _TestCoglboxPrivate +{ + CoglHandle texhand_id; + CoglHandle texture_id; + CoglHandle offscreen_id; +}; + +/* Coglbox implementation + *--------------------------------------------------*/ + +static void +test_coglbox_paint(ClutterActor *self) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); + + ClutterColor cback = {0x66, 0x66, 0xDD, 0xFF}; + ClutterColor cred = {0xFF, 0x0, 0x0, 0xFF}; + ClutterColor cgreen = {0x0, 0xFF, 0x0, 0xFF}; + ClutterColor cfullopaque = {0xFF, 0xFF, 0xFF, 0xFF}; + ClutterColor chalfopaque = {0xFF, 0xFF, 0xFF, 0x88}; + + ClutterFixed texcoords[4] = { + CLUTTER_FLOAT_TO_FIXED (0.0f), + CLUTTER_FLOAT_TO_FIXED (0.0f), + CLUTTER_FLOAT_TO_FIXED (1.0f), + CLUTTER_FLOAT_TO_FIXED (1.0f) + }; + + priv = TEST_COGLBOX_GET_PRIVATE (self); + + cogl_color (&cback); + cogl_fast_fill_rectangle (0,0,400,400); + + cogl_color (&cfullopaque); + cogl_texture_rectangle (priv->texhand_id, + 0,0, + CLUTTER_INT_TO_FIXED (400), + CLUTTER_INT_TO_FIXED (400), + 0,0, + CLUTTER_INT_TO_FIXED (6), + CLUTTER_INT_TO_FIXED (6)); + + cogl_draw_buffer (COGL_OFFSCREEN_BUFFER, priv->offscreen_id); + + cogl_color (&cred); + cogl_fast_fill_rectangle (20,20,100,100); + + cogl_color (&cgreen); + cogl_fast_fill_rectangle (80,80,100,100); + + cogl_draw_buffer (COGL_WINDOW_BUFFER, 0); + + cogl_color (&chalfopaque); + cogl_texture_rectangle (priv->texture_id, + CLUTTER_INT_TO_FIXED (100), + CLUTTER_INT_TO_FIXED (100), + CLUTTER_INT_TO_FIXED (300), + CLUTTER_INT_TO_FIXED (300), + texcoords[0], + texcoords[1], + texcoords[2], + texcoords[3]); +} + +static void +test_coglbox_finalize (GObject *object) +{ + G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); +} + +static void +test_coglbox_dispose (GObject *object) +{ + TestCoglboxPrivate *priv; + + priv = TEST_COGLBOX_GET_PRIVATE (object); + + cogl_texture_unref (priv->texture_id); + cogl_offscreen_unref (priv->offscreen_id); + + G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); +} + +static void +test_coglbox_init (TestCoglbox *self) +{ + TestCoglboxPrivate *priv; + self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); + + printf ("Loading redhand.png\n"); + priv->texhand_id = cogl_texture_new_from_file ("redhand.png", 0, + COGL_PIXEL_FORMAT_ANY, + NULL); + + printf ("Creating texture with size\n"); + priv->texture_id = cogl_texture_new_with_size (200,200,0, + COGL_PIXEL_FORMAT_RGB_888); + + if (priv->texture_id == COGL_INVALID_HANDLE) + printf ("Failed creating texture with size!\n"); + + printf ("Creating offscreen\n"); + priv->offscreen_id = cogl_offscreen_new_to_texture (priv->texture_id); + + if (priv->offscreen_id == COGL_INVALID_HANDLE) + printf ("Failed creating offscreen to texture!\n"); +} + +static void +test_coglbox_class_init (TestCoglboxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->finalize = test_coglbox_finalize; + gobject_class->dispose = test_coglbox_dispose; + actor_class->paint = test_coglbox_paint; + + g_type_class_add_private (gobject_class, sizeof (TestCoglboxPrivate)); +} + +ClutterActor* +test_coglbox_new (void) +{ + return g_object_new (TEST_TYPE_COGLBOX, NULL); +} + +int +main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterActor *coglbox; + ClutterTimeline *timeline; + ClutterBehaviour *behave; + ClutterAlpha *alpha; + + clutter_init(&argc, &argv); + + /* Stage */ + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Test"); + + /* Cogl Box */ + coglbox = test_coglbox_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); + + clutter_actor_show_all (stage); + + clutter_main (); + + return 0; +} diff --git a/tests/test-cogl-primitives.c b/tests/test-cogl-primitives.c new file mode 100644 index 000000000..2e1d270d5 --- /dev/null +++ b/tests/test-cogl-primitives.c @@ -0,0 +1,321 @@ +#include +#include +#include +#include +#include + +/* Coglbox declaration + *--------------------------------------------------*/ + +G_BEGIN_DECLS + +#define TEST_TYPE_COGLBOX test_coglbox_get_type() + +#define TEST_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_IS_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TEST_TYPE_COGLBOX)) + +#define TEST_IS_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TEST_TYPE_COGLBOX)) + +#define TEST_COGLBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +typedef struct _TestCoglbox TestCoglbox; +typedef struct _TestCoglboxClass TestCoglboxClass; +typedef struct _TestCoglboxPrivate TestCoglboxPrivate; + +struct _TestCoglbox +{ + ClutterActor parent; + + /*< private >*/ + TestCoglboxPrivate *priv; +}; + +struct _TestCoglboxClass +{ + ClutterActorClass parent_class; + + /* padding for future expansion */ + void (*_test_coglbox1) (void); + void (*_test_coglbox2) (void); + void (*_test_coglbox3) (void); + void (*_test_coglbox4) (void); +}; + +GType test_coglbox_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +/* Coglbox private declaration + *--------------------------------------------------*/ + +G_DEFINE_TYPE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); + +#define TEST_COGLBOX_GET_PRIVATE(obj) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TEST_TYPE_COGLBOX, TestCoglboxPrivate)) + +struct _TestCoglboxPrivate +{ + void (*_test_coglbox_priv1) (void); +}; + +/* Coglbox implementation + *--------------------------------------------------*/ + +typedef void (*PaintFunc) (void); + +static void +test_paint_line () +{ + cogl_line (CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (-25), + CLUTTER_INT_TO_FIXED (50), + CLUTTER_INT_TO_FIXED (25)); +} + +static void +test_paint_rect () +{ + cogl_rectangle (CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (-25), + CLUTTER_INT_TO_FIXED (100), + CLUTTER_INT_TO_FIXED (50)); +} + +static void +test_paint_rndrect() +{ + cogl_round_rectangle (CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (-25), + CLUTTER_INT_TO_FIXED (100), + CLUTTER_INT_TO_FIXED (50), + CLUTTER_INT_TO_FIXED (10), + 5); +} + +static void +test_paint_polyl () +{ + ClutterFixed poly_coords[] = { + CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (+50), + CLUTTER_INT_TO_FIXED (-30), + CLUTTER_INT_TO_FIXED (+30), + CLUTTER_INT_TO_FIXED (+30), + CLUTTER_INT_TO_FIXED (-30), + CLUTTER_INT_TO_FIXED (+40) + }; + + cogl_polyline (poly_coords, 4); +} + +static void +test_paint_polyg () +{ + gint poly_coords[] = { + CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (+50), + CLUTTER_INT_TO_FIXED (-30), + CLUTTER_INT_TO_FIXED (+30), + CLUTTER_INT_TO_FIXED (+30), + CLUTTER_INT_TO_FIXED (-30), + CLUTTER_INT_TO_FIXED (+40) + }; + + cogl_polygon (poly_coords, 4); +} + +static void +test_paint_arc () +{ + cogl_arc (0,0, + CLUTTER_INT_TO_FIXED (60), + CLUTTER_INT_TO_FIXED (40), + CLUTTER_ANGLE_FROM_DEG (-45), + CLUTTER_ANGLE_FROM_DEG (+45), + 10); +} + +static void +test_paint_elp () +{ + cogl_ellipse (0, 0, + CLUTTER_INT_TO_FIXED (60), + CLUTTER_INT_TO_FIXED (40), + 10); +} + +static void +test_paint_bezier2 () +{ + cogl_path_move_to (CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (+25)); + + cogl_path_bezier2_to (CLUTTER_INT_TO_FIXED (0), + CLUTTER_INT_TO_FIXED (-25), + CLUTTER_INT_TO_FIXED (+50), + CLUTTER_INT_TO_FIXED (+25)); +} + +static void +test_paint_bezier3 () +{ + cogl_path_move_to (CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (+50)); + + cogl_path_bezier3_to (CLUTTER_INT_TO_FIXED (+100), + CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (-100), + CLUTTER_INT_TO_FIXED (-50), + CLUTTER_INT_TO_FIXED (+50), + CLUTTER_INT_TO_FIXED (+50)); +} + +static void +test_coglbox_paint(ClutterActor *self) +{ + TestCoglboxPrivate *priv; + + ClutterColor cfill; + ClutterColor cstroke; + + static GTimer *timer = NULL; + static gint paint_index = 0; + + const gint NUM_PAINT_FUNCS = 9; + PaintFunc paint_func [NUM_PAINT_FUNCS]; + + priv = TEST_COGLBOX_GET_PRIVATE (self); + + paint_func[0] = test_paint_line; + paint_func[1] = test_paint_rect; + paint_func[2] = test_paint_rndrect; + paint_func[3] = test_paint_polyl; + paint_func[4] = test_paint_polyg; + paint_func[5] = test_paint_arc; + paint_func[6] = test_paint_elp; + paint_func[7] = test_paint_bezier2; + paint_func[8] = test_paint_bezier3; + + if (!timer) + { + timer = g_timer_new (); + g_timer_start (timer); + } + + if (g_timer_elapsed (timer, NULL) >= 1) + { + paint_index = (++paint_index) % NUM_PAINT_FUNCS; + g_timer_start (timer); + } + + cfill.red = 0; + cfill.green = 160; + cfill.blue = 0; + cfill.alpha = 255; + + cstroke.red = 200; + cstroke.green = 0; + cstroke.blue = 0; + cstroke.alpha = 255; + + cogl_push_matrix (); + + paint_func[paint_index] (); + + cogl_translate (100,100,0); + cogl_color (&cstroke); + cogl_stroke (); + + cogl_translate (150,0,0); + cogl_color (&cfill); + cogl_fill (); + + cogl_pop_matrix(); +} + +static void +test_coglbox_finalize (GObject *object) +{ + G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); +} + +static void +test_coglbox_dispose (GObject *object) +{ + TestCoglboxPrivate *priv; + + priv = TEST_COGLBOX_GET_PRIVATE (object); + + G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); +} + +static void +test_coglbox_init (TestCoglbox *self) +{ + TestCoglboxPrivate *priv; + self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); +} + +static void +test_coglbox_class_init (TestCoglboxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->finalize = test_coglbox_finalize; + gobject_class->dispose = test_coglbox_dispose; + actor_class->paint = test_coglbox_paint; + + g_type_class_add_private (gobject_class, sizeof (TestCoglboxPrivate)); +} + +ClutterActor* +test_coglbox_new (void) +{ + return g_object_new (TEST_TYPE_COGLBOX, NULL); +} + +#define SPIN() while (g_main_context_pending (NULL)) \ + g_main_context_iteration (NULL, FALSE); + +int +main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterActor *coglbox; + + clutter_init(&argc, &argv); + + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Test"); + + coglbox = test_coglbox_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); + + clutter_actor_show_all (stage); + + while (1) + { + clutter_actor_hide (coglbox); + clutter_actor_show (coglbox); + SPIN(); + } + + return 0; +} diff --git a/tests/test-cogl-tex-convert.c b/tests/test-cogl-tex-convert.c new file mode 100644 index 000000000..a602c6efc --- /dev/null +++ b/tests/test-cogl-tex-convert.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include + +/* Coglbox declaration + *--------------------------------------------------*/ + +G_BEGIN_DECLS + +#define TEST_TYPE_COGLBOX test_coglbox_get_type() + +#define TEST_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_IS_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TEST_TYPE_COGLBOX)) + +#define TEST_IS_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TEST_TYPE_COGLBOX)) + +#define TEST_COGLBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +typedef struct _TestCoglbox TestCoglbox; +typedef struct _TestCoglboxClass TestCoglboxClass; +typedef struct _TestCoglboxPrivate TestCoglboxPrivate; + +struct _TestCoglbox +{ + ClutterActor parent; + + /*< private >*/ + TestCoglboxPrivate *priv; +}; + +struct _TestCoglboxClass +{ + ClutterActorClass parent_class; + + /* padding for future expansion */ + void (*_test_coglbox1) (void); + void (*_test_coglbox2) (void); + void (*_test_coglbox3) (void); + void (*_test_coglbox4) (void); +}; + +GType test_coglbox_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +/* Coglbox private declaration + *--------------------------------------------------*/ + +G_DEFINE_TYPE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); + +#define TEST_COGLBOX_GET_PRIVATE(obj) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TEST_TYPE_COGLBOX, TestCoglboxPrivate)) + +struct _TestCoglboxPrivate +{ + CoglHandle cogl_tex_id[4]; + gint frame; +}; + +/* Coglbox implementation + *--------------------------------------------------*/ + +static void +test_coglbox_paint(ClutterActor *self) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); + + ClutterColor cback = {0x66, 0x66, 0xDD, 0xFF}; + ClutterColor cwhite = {0xFF, 0xFF, 0xFF, 0xFF}; + ClutterFixed texcoords[4] = { + CLUTTER_FLOAT_TO_FIXED (0.0f), + CLUTTER_FLOAT_TO_FIXED (0.0f), + CLUTTER_FLOAT_TO_FIXED (1.0f), + CLUTTER_FLOAT_TO_FIXED (1.0f) + }; + + priv = TEST_COGLBOX_GET_PRIVATE (self); + + cogl_color (&cback); + cogl_fast_fill_rectangle (0,0,400,400); + + cogl_color (&cwhite); + + cogl_push_matrix (); + cogl_texture_rectangle (priv->cogl_tex_id[0], + 0, 0, + CLUTTER_INT_TO_FIXED (200), + CLUTTER_INT_TO_FIXED (213), + texcoords[0], texcoords[1], + texcoords[2], texcoords[3]); + + cogl_pop_matrix (); + cogl_push_matrix (); + cogl_translate (200,0,0); + cogl_texture_rectangle (priv->cogl_tex_id[1], + 0, 0, + CLUTTER_INT_TO_FIXED (200), + CLUTTER_INT_TO_FIXED (213), + texcoords[0], texcoords[1], + texcoords[2], texcoords[3]); + + cogl_pop_matrix (); + cogl_push_matrix (); + cogl_translate (0,200,0); + cogl_texture_rectangle (priv->cogl_tex_id[2], + 0, 0, + CLUTTER_INT_TO_FIXED (200), + CLUTTER_INT_TO_FIXED (213), + texcoords[0], texcoords[1], + texcoords[2], texcoords[3]); + + cogl_pop_matrix (); + cogl_push_matrix (); + cogl_translate (200,200,0); + cogl_texture_rectangle (priv->cogl_tex_id[3], + 0, 0, + CLUTTER_INT_TO_FIXED (200), + CLUTTER_INT_TO_FIXED (213), + texcoords[0], texcoords[1], + texcoords[2], texcoords[3]); + + cogl_pop_matrix(); +} + +static void +test_coglbox_finalize (GObject *object) +{ + G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); +} + +static void +test_coglbox_dispose (GObject *object) +{ + TestCoglboxPrivate *priv; + + priv = TEST_COGLBOX_GET_PRIVATE (object); + cogl_texture_unref (priv->cogl_tex_id); + + G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); +} + +static void +test_coglbox_init (TestCoglbox *self) +{ + TestCoglboxPrivate *priv; + self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); + + priv->cogl_tex_id[0] = + cogl_texture_new_from_file ("redhand.png", 0, + COGL_PIXEL_FORMAT_ANY, NULL); + + priv->cogl_tex_id[1] = + cogl_texture_new_from_file ("redhand.png", 0, + COGL_PIXEL_FORMAT_BGRA_8888, NULL); + + priv->cogl_tex_id[2] = + cogl_texture_new_from_file ("redhand.png", 0, + COGL_PIXEL_FORMAT_ARGB_8888, NULL); + + priv->cogl_tex_id[3] = + cogl_texture_new_from_file ("redhand.png", 0, + COGL_PIXEL_FORMAT_G_8, NULL); +} + +static void +test_coglbox_class_init (TestCoglboxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->finalize = test_coglbox_finalize; + gobject_class->dispose = test_coglbox_dispose; + actor_class->paint = test_coglbox_paint; + + g_type_class_add_private (gobject_class, sizeof (TestCoglboxPrivate)); +} + +ClutterActor* +test_coglbox_new (void) +{ + return g_object_new (TEST_TYPE_COGLBOX, NULL); +} + +static void +frame_cb (ClutterTimeline *timeline, + gint frame_num, + gpointer data) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (data); + + priv->frame = frame_num; + clutter_actor_queue_redraw (CLUTTER_ACTOR (data)); +} + +int +main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterActor *coglbox; + ClutterTimeline *timeline; + ClutterBehaviour *behave; + ClutterAlpha *alpha; + + clutter_init(&argc, &argv); + + /* Stage */ + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Test"); + + /* Cogl Box */ + coglbox = test_coglbox_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); + + clutter_actor_show_all (stage); + + clutter_main (); + + return 0; +} diff --git a/tests/test-cogl-tex-foreign.c b/tests/test-cogl-tex-foreign.c new file mode 100644 index 000000000..f887a636c --- /dev/null +++ b/tests/test-cogl-tex-foreign.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include + +/* Coglbox declaration + *--------------------------------------------------*/ + +G_BEGIN_DECLS + +#define TEST_TYPE_COGLBOX test_coglbox_get_type() + +#define TEST_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_IS_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TEST_TYPE_COGLBOX)) + +#define TEST_IS_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TEST_TYPE_COGLBOX)) + +#define TEST_COGLBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +typedef struct _TestCoglbox TestCoglbox; +typedef struct _TestCoglboxClass TestCoglboxClass; +typedef struct _TestCoglboxPrivate TestCoglboxPrivate; + +struct _TestCoglbox +{ + ClutterActor parent; + + /*< private >*/ + TestCoglboxPrivate *priv; +}; + +struct _TestCoglboxClass +{ + ClutterActorClass parent_class; + + /* padding for future expansion */ + void (*_test_coglbox1) (void); + void (*_test_coglbox2) (void); + void (*_test_coglbox3) (void); + void (*_test_coglbox4) (void); +}; + +GType test_coglbox_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +/* Coglbox private declaration + *--------------------------------------------------*/ + +G_DEFINE_TYPE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); + +#define TEST_COGLBOX_GET_PRIVATE(obj) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TEST_TYPE_COGLBOX, TestCoglboxPrivate)) + +struct _TestCoglboxPrivate +{ + GLuint gl_handle; + CoglHandle cogl_handle; +}; + +/* Coglbox implementation + *--------------------------------------------------*/ + +static void +test_coglbox_paint(ClutterActor *self) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); + + ClutterColor cback = {0x66, 0x66, 0xDD, 0xFF}; + ClutterColor cwhite = {0xFF, 0xFF, 0xFF, 0xFF}; + ClutterFixed texcoords[4] = { + CLUTTER_FLOAT_TO_FIXED (0.3f), + CLUTTER_FLOAT_TO_FIXED (0.3f), + CLUTTER_FLOAT_TO_FIXED (0.7f), + CLUTTER_FLOAT_TO_FIXED (0.7f) + }; + + priv = TEST_COGLBOX_GET_PRIVATE (self); + + cogl_color (&cback); + cogl_fast_fill_rectangle (0,0,400,400); + + cogl_color (&cwhite); + + cogl_push_matrix (); + + cogl_translate (100,100,0); + cogl_texture_rectangle (priv->cogl_handle, + 0, 0, + CLUTTER_INT_TO_FIXED (200), + CLUTTER_INT_TO_FIXED (200), + texcoords[0], texcoords[1], + texcoords[2], texcoords[3]); + + cogl_pop_matrix(); +} + +static void +test_coglbox_finalize (GObject *object) +{ + G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); +} + +static void +test_coglbox_dispose (GObject *object) +{ + TestCoglboxPrivate *priv; + + priv = TEST_COGLBOX_GET_PRIVATE (object); + + cogl_texture_unref (priv->cogl_handle); + glDeleteTextures (1, &priv->gl_handle); + + G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); +} + +static void +test_coglbox_init (TestCoglbox *self) +{ + TestCoglboxPrivate *priv; + guchar data[12]; + + self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); + + /* Prepare a 2x2 pixels texture */ + + data[0] = 255; data[1] = 0; data[2] = 0; + data[3] = 0; data[4] = 255; data[5] = 0; + data[6] = 0; data[7] = 0; data[8] = 255; + data[9] = 0; data[10] = 0; data[11] = 0; + + glGenTextures (1, &priv->gl_handle); + glBindTexture (GL_TEXTURE_2D, priv->gl_handle); + + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, + 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + /* Create texture from foreign */ + + priv->cogl_handle = + cogl_texture_new_from_foreign (priv->gl_handle, + GL_TEXTURE_2D, + 2, 2, 0, 0, + COGL_PIXEL_FORMAT_RGB_888); + + if (priv->cogl_handle == COGL_INVALID_HANDLE) + { + printf ("Failed creating texture from foreign!\n"); + return; + } +} + +static void +test_coglbox_class_init (TestCoglboxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->finalize = test_coglbox_finalize; + gobject_class->dispose = test_coglbox_dispose; + actor_class->paint = test_coglbox_paint; + + g_type_class_add_private (gobject_class, sizeof (TestCoglboxPrivate)); +} + +ClutterActor* +test_coglbox_new (void) +{ + return g_object_new (TEST_TYPE_COGLBOX, NULL); +} + +int +main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterActor *coglbox; + ClutterTimeline *timeline; + ClutterBehaviour *behave; + ClutterAlpha *alpha; + + clutter_init(&argc, &argv); + + /* Stage */ + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Test"); + + /* Cogl Box */ + coglbox = test_coglbox_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); + + clutter_actor_show_all (stage); + + clutter_main (); + + return 0; +} diff --git a/tests/test-cogl-tex-getset.c b/tests/test-cogl-tex-getset.c new file mode 100644 index 000000000..6ef9415cf --- /dev/null +++ b/tests/test-cogl-tex-getset.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include + +/* Coglbox declaration + *--------------------------------------------------*/ + +G_BEGIN_DECLS + +#define TEST_TYPE_COGLBOX test_coglbox_get_type() + +#define TEST_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_IS_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TEST_TYPE_COGLBOX)) + +#define TEST_IS_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TEST_TYPE_COGLBOX)) + +#define TEST_COGLBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +typedef struct _TestCoglbox TestCoglbox; +typedef struct _TestCoglboxClass TestCoglboxClass; +typedef struct _TestCoglboxPrivate TestCoglboxPrivate; + +struct _TestCoglbox +{ + ClutterActor parent; + + /*< private >*/ + TestCoglboxPrivate *priv; +}; + +struct _TestCoglboxClass +{ + ClutterActorClass parent_class; + + /* padding for future expansion */ + void (*_test_coglbox1) (void); + void (*_test_coglbox2) (void); + void (*_test_coglbox3) (void); + void (*_test_coglbox4) (void); +}; + +GType test_coglbox_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +/* Coglbox private declaration + *--------------------------------------------------*/ + +G_DEFINE_TYPE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); + +#define TEST_COGLBOX_GET_PRIVATE(obj) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TEST_TYPE_COGLBOX, TestCoglboxPrivate)) + +struct _TestCoglboxPrivate +{ + CoglHandle cogl_tex_id[4]; +}; + +/* Coglbox implementation + *--------------------------------------------------*/ + +static void +test_coglbox_paint(ClutterActor *self) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); + + ClutterColor cback = {0x66, 0x66, 0xDD, 0xFF}; + ClutterColor cwhite = {0xFF, 0xFF, 0xFF, 0xFF}; + ClutterFixed texcoords[4] = { + CLUTTER_FLOAT_TO_FIXED (0.0f), + CLUTTER_FLOAT_TO_FIXED (0.0f), + CLUTTER_FLOAT_TO_FIXED (1.0f), + CLUTTER_FLOAT_TO_FIXED (1.0f) + }; + + priv = TEST_COGLBOX_GET_PRIVATE (self); + + cogl_color (&cback); + cogl_fast_fill_rectangle (0,0,400,400); + + cogl_color (&cwhite); + + cogl_push_matrix (); + + cogl_translate (100,100,0); + cogl_texture_rectangle (priv->cogl_tex_id[1], + 0, 0, + CLUTTER_INT_TO_FIXED (200), + CLUTTER_INT_TO_FIXED (213), + texcoords[0], texcoords[1], + texcoords[2], texcoords[3]); + + cogl_pop_matrix(); +} + +static void +test_coglbox_finalize (GObject *object) +{ + G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); +} + +static void +test_coglbox_dispose (GObject *object) +{ + TestCoglboxPrivate *priv; + + priv = TEST_COGLBOX_GET_PRIVATE (object); + cogl_texture_unref (priv->cogl_tex_id); + + G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); +} + +static void +test_coglbox_init (TestCoglbox *self) +{ + TestCoglboxPrivate *priv; + guint width; + guint height; + CoglPixelFormat format; + gint size; + guchar *data; + gint x,y,t; + guchar *pixel; + + self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); + + /* Load image from file */ + + priv->cogl_tex_id[0] = + cogl_texture_new_from_file ("redhand.png", 40, COGL_PIXEL_FORMAT_ANY, NULL); + + if (priv->cogl_tex_id[0] == COGL_INVALID_HANDLE) + { + printf ("Failed loading redhand.png image!\n"); + return; + } + + printf("Texture loaded from file.\n"); + + /* Obtain pixel data */ + + format = COGL_PIXEL_FORMAT_RGBA_8888; + width = cogl_texture_get_width (priv->cogl_tex_id[0]); + height = cogl_texture_get_height (priv->cogl_tex_id[0]); + size = cogl_texture_get_data (priv->cogl_tex_id[0], + format, 0, NULL); + + printf("size: %dx%d\n", width, height); + printf("format: 0x%x\n", format); + printf("bytesize: %d\n", size); + + data = (guchar*) g_malloc (sizeof(guchar) * size); + + cogl_texture_get_data (priv->cogl_tex_id[0], + format, 0, data); + + /* Create new texture from modified data */ + + priv->cogl_tex_id[1] = + cogl_texture_new_from_data (width, height, 0, + format, format, + 0, data); + + if (priv->cogl_tex_id[1] == COGL_INVALID_HANDLE) + { + printf ("Failed creating image from data!\n"); + return; + } + + printf ("Texture created from data.\n"); + + /* Modify data (swap red and green) */ + + for (y=0; ycogl_tex_id[1], + 0, 0, 0, 0, + 100, 100, width, height, + format, 0, data); + + cogl_texture_set_region (priv->cogl_tex_id[1], + 100, 100, 100, 100, + 100, 100, width, height, + format, 0, data); + + printf ("Subregion data updated.\n"); +} + +static void +test_coglbox_class_init (TestCoglboxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->finalize = test_coglbox_finalize; + gobject_class->dispose = test_coglbox_dispose; + actor_class->paint = test_coglbox_paint; + + g_type_class_add_private (gobject_class, sizeof (TestCoglboxPrivate)); +} + +ClutterActor* +test_coglbox_new (void) +{ + return g_object_new (TEST_TYPE_COGLBOX, NULL); +} + +int +main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterActor *coglbox; + ClutterTimeline *timeline; + ClutterBehaviour *behave; + ClutterAlpha *alpha; + + clutter_init(&argc, &argv); + + /* Stage */ + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Test"); + + /* Cogl Box */ + coglbox = test_coglbox_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); + + clutter_actor_show_all (stage); + + clutter_main (); + + return 0; +} diff --git a/tests/test-cogl-tex-polygon.c b/tests/test-cogl-tex-polygon.c new file mode 100644 index 000000000..0bbb23b2a --- /dev/null +++ b/tests/test-cogl-tex-polygon.c @@ -0,0 +1,319 @@ +#include +#include +#include +#include +#include + +/* Coglbox declaration + *--------------------------------------------------*/ + +G_BEGIN_DECLS + +#define TEST_TYPE_COGLBOX test_coglbox_get_type() + +#define TEST_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_IS_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TEST_TYPE_COGLBOX)) + +#define TEST_IS_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TEST_TYPE_COGLBOX)) + +#define TEST_COGLBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +typedef struct _TestCoglbox TestCoglbox; +typedef struct _TestCoglboxClass TestCoglboxClass; +typedef struct _TestCoglboxPrivate TestCoglboxPrivate; + +struct _TestCoglbox +{ + ClutterActor parent; + + /*< private >*/ + TestCoglboxPrivate *priv; +}; + +struct _TestCoglboxClass +{ + ClutterActorClass parent_class; + + /* padding for future expansion */ + void (*_test_coglbox1) (void); + void (*_test_coglbox2) (void); + void (*_test_coglbox3) (void); + void (*_test_coglbox4) (void); +}; + +GType test_coglbox_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +/* Coglbox private declaration + *--------------------------------------------------*/ + +G_DEFINE_TYPE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); + +#define TEST_COGLBOX_GET_PRIVATE(obj) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TEST_TYPE_COGLBOX, TestCoglboxPrivate)) + +struct _TestCoglboxPrivate +{ + CoglHandle cogl_tex_id; + gint frame; +}; + +/* Coglbox implementation + *--------------------------------------------------*/ + +static void +test_coglbox_fade_texture (CoglHandle tex_id, + ClutterFixed x1, + ClutterFixed y1, + ClutterFixed x2, + ClutterFixed y2, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2) +{ + CoglTextureVertex vertices[4]; + int i; + static const ClutterColor white = { 0xff, 0xff, 0xff, 0xff }; + + vertices[0].x = x1; + vertices[0].y = y1; + vertices[0].z = 0; + vertices[0].tx = tx1; + vertices[0].ty = ty1; + vertices[1].x = x1; + vertices[1].y = y2; + vertices[1].z = 0; + vertices[1].tx = tx1; + vertices[1].ty = ty2; + vertices[2].x = x2; + vertices[2].y = y2; + vertices[2].z = 0; + vertices[2].tx = tx2; + vertices[2].ty = ty2; + vertices[3].x = x2; + vertices[3].y = y1; + vertices[3].z = 0; + vertices[3].tx = tx2; + vertices[3].ty = ty1; + + for (i = 0; i < 4; i++) + { + vertices[i].color.red = 255; + vertices[i].color.green = 255; + vertices[i].color.blue = 255; + vertices[i].color.alpha = ((i ^ (i >> 1)) & 1) ? 0 : 128; + } + + cogl_texture_polygon (tex_id, 4, vertices, TRUE); + + cogl_color (&white); +} + +static void +test_coglbox_triangle_texture (CoglHandle tex_id, + ClutterFixed x, + ClutterFixed y, + ClutterFixed tx1, + ClutterFixed ty1, + ClutterFixed tx2, + ClutterFixed ty2, + ClutterFixed tx3, + ClutterFixed ty3) +{ + CoglTextureVertex vertices[3]; + int tex_width = cogl_texture_get_width (tex_id); + int tex_height = cogl_texture_get_height (tex_id); + + vertices[0].x = x + tx1 * tex_width; + vertices[0].y = y + ty1 * tex_height; + vertices[0].z = 0; + vertices[0].tx = tx1; + vertices[0].ty = ty1; + + vertices[1].x = x + tx2 * tex_width; + vertices[1].y = y + ty2 * tex_height; + vertices[1].z = 0; + vertices[1].tx = tx2; + vertices[1].ty = ty2; + + vertices[2].x = x + tx3 * tex_width; + vertices[2].y = y + ty3 * tex_height; + vertices[2].z = 0; + vertices[2].tx = tx3; + vertices[2].ty = ty3; + + cogl_texture_polygon (tex_id, 3, vertices, FALSE); +} + +static void +test_coglbox_paint (ClutterActor *self) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); + int tex_width = cogl_texture_get_width (priv->cogl_tex_id); + int tex_height = cogl_texture_get_height (priv->cogl_tex_id); + int i; + + for (i = 0; i < 2; i++) + { + cogl_texture_set_filters (priv->cogl_tex_id, + i ? CGL_LINEAR : CGL_NEAREST, + i ? CGL_LINEAR : CGL_NEAREST); + + if (i) + { + cogl_push_matrix (); + cogl_translate (tex_width, tex_height, 0); + } + + cogl_push_matrix (); + cogl_translate (tex_width / 2, 0, 0); + cogl_rotate (priv->frame, 0, 1, 0); + cogl_translate (-tex_width / 2, 0, 0); + + /* Draw a hand and refect it */ + cogl_texture_rectangle (priv->cogl_tex_id, + 0, 0, + CLUTTER_INT_TO_FIXED (tex_width), + CLUTTER_INT_TO_FIXED (tex_height), + 0, 0, CFX_ONE, CFX_ONE); + test_coglbox_fade_texture (priv->cogl_tex_id, + 0, CLUTTER_INT_TO_FIXED (tex_height), + CLUTTER_INT_TO_FIXED (tex_width), + CLUTTER_INT_TO_FIXED (tex_height * 3 / 2), + 0, CFX_ONE, + CFX_ONE, CFX_ONE / 2); + + cogl_pop_matrix (); + + cogl_push_matrix (); + cogl_translate (tex_width * 3 / 2 + 60, 0, 0); + cogl_rotate (priv->frame, 0, 1, 0); + cogl_translate (-tex_width / 2 - 10, 0, 0); + + /* Draw the texture split into two triangles */ + test_coglbox_triangle_texture (priv->cogl_tex_id, + 0, 0, + 0, 0, + 0, CFX_ONE, + CFX_ONE, CFX_ONE); + test_coglbox_triangle_texture (priv->cogl_tex_id, + CLUTTER_INT_TO_FIXED (20), 0, + 0, 0, + CFX_ONE, 0, + CFX_ONE, CFX_ONE); + + cogl_pop_matrix (); + + if (i) + cogl_pop_matrix (); + } +} + +static void +test_coglbox_finalize (GObject *object) +{ + G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); +} + +static void +test_coglbox_dispose (GObject *object) +{ + TestCoglboxPrivate *priv; + + priv = TEST_COGLBOX_GET_PRIVATE (object); + cogl_texture_unref (priv->cogl_tex_id); + + G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); +} + +static void +test_coglbox_init (TestCoglbox *self) +{ + TestCoglboxPrivate *priv; + self->priv = priv = TEST_COGLBOX_GET_PRIVATE (self); + + priv->cogl_tex_id = cogl_texture_new_from_file ("redhand.png", 0, + COGL_PIXEL_FORMAT_ANY, + NULL); + + cogl_texture_set_filters (priv->cogl_tex_id, + CGL_LINEAR, CGL_LINEAR); +} + +static void +test_coglbox_class_init (TestCoglboxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->finalize = test_coglbox_finalize; + gobject_class->dispose = test_coglbox_dispose; + actor_class->paint = test_coglbox_paint; + + g_type_class_add_private (gobject_class, sizeof (TestCoglboxPrivate)); +} + +ClutterActor* +test_coglbox_new (void) +{ + return g_object_new (TEST_TYPE_COGLBOX, NULL); +} + +static void +frame_cb (ClutterTimeline *timeline, + gint frame_num, + gpointer data) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (data); + + priv->frame = frame_num; + clutter_actor_queue_redraw (CLUTTER_ACTOR (data)); +} + +int +main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterActor *coglbox; + ClutterTimeline *timeline; + ClutterColor blue = { 0x30, 0x30, 0xff, 0xff }; + + clutter_init (&argc, &argv); + + /* Stage */ + stage = clutter_stage_get_default (); + clutter_stage_set_color (CLUTTER_STAGE (stage), &blue); + clutter_actor_set_size (stage, 800, 600); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Test"); + + /* Cogl Box */ + coglbox = test_coglbox_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); + + /* Timeline for animation */ + timeline = clutter_timeline_new (360, 60); /* num frames, fps */ + g_object_set (timeline, "loop", TRUE, NULL); /* have it loop */ + g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), coglbox); + clutter_timeline_start (timeline); + + clutter_actor_show_all (stage); + + clutter_main (); + + return 0; +} diff --git a/tests/test-cogl-tex-tile.c b/tests/test-cogl-tex-tile.c new file mode 100644 index 000000000..7ce75056e --- /dev/null +++ b/tests/test-cogl-tex-tile.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include + +/* Coglbox declaration + *--------------------------------------------------*/ + +G_BEGIN_DECLS + +#define TEST_TYPE_COGLBOX test_coglbox_get_type() + +#define TEST_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +#define TEST_IS_COGLBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + TEST_TYPE_COGLBOX)) + +#define TEST_IS_COGLBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + TEST_TYPE_COGLBOX)) + +#define TEST_COGLBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + TEST_TYPE_COGLBOX, TestCoglboxClass)) + +typedef struct _TestCoglbox TestCoglbox; +typedef struct _TestCoglboxClass TestCoglboxClass; +typedef struct _TestCoglboxPrivate TestCoglboxPrivate; + +struct _TestCoglbox +{ + ClutterActor parent; + + /*< private >*/ + TestCoglboxPrivate *priv; +}; + +struct _TestCoglboxClass +{ + ClutterActorClass parent_class; + + /* padding for future expansion */ + void (*_test_coglbox1) (void); + void (*_test_coglbox2) (void); + void (*_test_coglbox3) (void); + void (*_test_coglbox4) (void); +}; + +GType test_coglbox_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +/* Coglbox private declaration + *--------------------------------------------------*/ + +G_DEFINE_TYPE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); + +#define TEST_COGLBOX_GET_PRIVATE(obj) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((obj), TEST_TYPE_COGLBOX, TestCoglboxPrivate)) + +struct _TestCoglboxPrivate +{ + CoglHandle cogl_tex_id; + gint frame; +}; + +/* Coglbox implementation + *--------------------------------------------------*/ + +static void +test_coglbox_paint(ClutterActor *self) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); + + ClutterColor cback = {0x66, 0x66, 0xDD, 0xFF}; + ClutterColor cwhite = {0xFF, 0xFF, 0xFF, 0xFF}; + ClutterFixed texcoords[4] = { + CLUTTER_FLOAT_TO_FIXED (0.0f), + CLUTTER_FLOAT_TO_FIXED (0.0f), + CLUTTER_FLOAT_TO_FIXED (1.0f), + CLUTTER_FLOAT_TO_FIXED (1.0f) + }; + + gint pingpong_frame; + ClutterFixed sin_frame, cos_frame; + ClutterFixed frac_frame; + gint t; + + sin_frame = clutter_sini (CLUTTER_ANGLE_FROM_DEG (priv->frame)); + cos_frame = clutter_cosi (CLUTTER_ANGLE_FROM_DEG (priv->frame)); + + pingpong_frame = (priv->frame <= 180 ? priv->frame : 360 - priv->frame); + frac_frame = CFX_QDIV (CLUTTER_INT_TO_FIXED (pingpong_frame), + CLUTTER_INT_TO_FIXED (180)); + frac_frame += (CFX_ONE >> 1); + frac_frame <<= 1; + + for (t=0; t<4; t+=2) + { + texcoords[t] += cos_frame; + texcoords[t+1] += sin_frame; + + texcoords[t] = CFX_QMUL (texcoords[t], frac_frame); + texcoords[t+1] = CFX_QMUL (texcoords[t+1], frac_frame); + } + + priv = TEST_COGLBOX_GET_PRIVATE (self); + + cogl_push_matrix (); + + cogl_color (&cback); + cogl_fast_fill_rectangle (0,0,400,400); + + cogl_color (&cwhite); + cogl_translate (100,100,0); + cogl_texture_rectangle (priv->cogl_tex_id, + 0, 0, + CLUTTER_INT_TO_FIXED (200), + CLUTTER_INT_TO_FIXED (213), + texcoords[0], texcoords[1], + texcoords[2], texcoords[3]); + + cogl_pop_matrix(); +} + +static void +test_coglbox_finalize (GObject *object) +{ + G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); +} + +static void +test_coglbox_dispose (GObject *object) +{ + TestCoglboxPrivate *priv; + + priv = TEST_COGLBOX_GET_PRIVATE (object); + cogl_texture_unref (priv->cogl_tex_id); + + G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); +} + +static void +test_coglbox_init (TestCoglbox *self) +{ + TestCoglboxPrivate *priv; + self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); + + priv->cogl_tex_id = cogl_texture_new_from_file ("redhand.png", 0, + COGL_PIXEL_FORMAT_ANY, + NULL); + + cogl_texture_set_filters (priv->cogl_tex_id, + CGL_LINEAR, CGL_LINEAR); +} + +static void +test_coglbox_class_init (TestCoglboxClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); + + gobject_class->finalize = test_coglbox_finalize; + gobject_class->dispose = test_coglbox_dispose; + actor_class->paint = test_coglbox_paint; + + g_type_class_add_private (gobject_class, sizeof (TestCoglboxPrivate)); +} + +ClutterActor* +test_coglbox_new (void) +{ + return g_object_new (TEST_TYPE_COGLBOX, NULL); +} + +static void +frame_cb (ClutterTimeline *timeline, + gint frame_num, + gpointer data) +{ + TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (data); + + priv->frame = frame_num; + clutter_actor_queue_redraw (CLUTTER_ACTOR (data)); +} + +int +main (int argc, char *argv[]) +{ + ClutterActor *stage; + ClutterActor *coglbox; + ClutterTimeline *timeline; + ClutterBehaviour *behave; + ClutterAlpha *alpha; + + clutter_init(&argc, &argv); + + /* Stage */ + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 400, 400); + clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Test"); + + /* Cogl Box */ + coglbox = test_coglbox_new (); + clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); + + /* Timeline for animation */ + timeline = clutter_timeline_new (360, 60); /* num frames, fps */ + g_object_set (timeline, "loop", TRUE, NULL); /* have it loop */ + g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), coglbox); + clutter_timeline_start (timeline); + + clutter_actor_show_all (stage); + + clutter_main (); + + return 0; +} diff --git a/tests/test-depth.c b/tests/test-depth.c index 03084262c..cc3a9aaaa 100644 --- a/tests/test-depth.c +++ b/tests/test-depth.c @@ -116,16 +116,10 @@ main (int argc, char *argv[]) ClutterActor *group, *hand, *label, *rect, *janus, *box; ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xff }; ClutterColor rect_color = { 0, 0, 0, 0x88 }; - GdkPixbuf *pixbuf; GError *error; clutter_init (&argc, &argv); - error = NULL; - pixbuf = gdk_pixbuf_new_from_file ("redhand.png", &error); - if (error) - g_error ("Unable to load redhand.png: %s", error->message); - stage = clutter_stage_get_default (); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); clutter_stage_set_use_fog (CLUTTER_STAGE (stage), TRUE); @@ -143,7 +137,10 @@ main (int argc, char *argv[]) clutter_actor_set_position (label, 120, 200); clutter_actor_show (label); - hand = clutter_texture_new_from_pixbuf (pixbuf); + error = NULL; + hand = clutter_texture_new_from_file ("redhand.png", &error); + if (error) + g_error ("Unable to load redhand.png: %s", error->message); clutter_actor_set_position (hand, 240, 100); clutter_actor_show (hand); diff --git a/tests/test-fbo.c b/tests/test-fbo.c index 91a61efd6..d4bf3c1f2 100644 --- a/tests/test-fbo.c +++ b/tests/test-fbo.c @@ -10,17 +10,15 @@ ClutterActor* make_source(void) { ClutterActor *source, *actor; - GdkPixbuf *pixbuf; GError *error = NULL; ClutterColor yellow = {0xff, 0xff, 0x00, 0xff}; - pixbuf = gdk_pixbuf_new_from_file ("redhand.png", &error); - if (!pixbuf) + source = clutter_group_new(); + actor = clutter_texture_new_from_file ("redhand.png", &error); + if (!actor) g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); - source = clutter_group_new(); - actor = clutter_texture_new_from_pixbuf (pixbuf); clutter_group_add (source, actor); actor = clutter_label_new_with_text ("Sans Bold 50px", "Clutter"); diff --git a/tests/test-multistage.c b/tests/test-multistage.c index 9dfbd9af7..81f67bfaa 100644 --- a/tests/test-multistage.c +++ b/tests/test-multistage.c @@ -21,7 +21,6 @@ on_button_press (ClutterActor *actor, gchar *stage_label, *win_title; ClutterColor color = { 0xdd, 0x33, 0xdd, 0xff }; ClutterColor white = { 0x99, 0x99, 0x99, 0xff }; - GdkPixbuf *pixb; ClutterTimeline *timeline; ClutterAlpha *alpha; ClutterBehaviour *r_behave; @@ -34,12 +33,11 @@ on_button_press (ClutterActor *actor, clutter_stage_set_color (CLUTTER_STAGE (new_stage), &color); clutter_actor_set_size (new_stage, 320, 240); - pixb = gdk_pixbuf_new_from_file ("redhand.png", NULL); + tex = clutter_texture_new_from_file ("redhand.png", NULL); - if (!pixb) + if (!tex) g_error("pixbuf load failed"); - tex = clutter_texture_new_from_pixbuf (pixb); clutter_actor_set_reactive (tex, TRUE); g_signal_connect (tex, "button-press-event", G_CALLBACK (tex_button_cb), NULL); diff --git a/tests/test-pixmap.c b/tests/test-pixmap.c index 1302a2375..a3f789e26 100644 --- a/tests/test-pixmap.c +++ b/tests/test-pixmap.c @@ -1,12 +1,13 @@ + + +#if HAVE_CLUTTER_GLX #include #include #include #include -#if HAVE_CLUTTER_GLX #include #include -#endif #include #include @@ -14,7 +15,7 @@ #define IMAGE "redhand.png" - +#ifdef USE_GDKPIXBUF static gboolean stage_press_cb (ClutterActor *actor, @@ -61,7 +62,7 @@ create_pixmap (guint *width, guint *height, guint *depth) h = gdk_pixbuf_get_height (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); - data = malloc (w * h * 4); + data = g_malloc (w * h * 4); image = XCreateImage (dpy, None, 32, @@ -134,10 +135,12 @@ create_pixmap (guint *width, guint *height, guint *depth) return pixmap; } +#endif int main (int argc, char **argv) { +#ifdef USE_GDKPIXBUF ClutterActor *stage, *tex; Pixmap pixmap; guint w, h, d; @@ -195,5 +198,10 @@ main (int argc, char **argv) clutter_actor_show (stage); clutter_main (); +#endif } + +#else +int main(int argc, char **argv){return 0;}; +#endif diff --git a/tests/test-rotate.c b/tests/test-rotate.c index 16967a05c..94e9ce2b6 100644 --- a/tests/test-rotate.c +++ b/tests/test-rotate.c @@ -15,22 +15,19 @@ main (int argc, char *argv[]) ClutterActor *stage; ClutterActor *hand, *label; ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xff }; - GdkPixbuf *pixbuf; clutter_init (&argc, &argv); stage = clutter_stage_get_default (); - pixbuf = gdk_pixbuf_new_from_file ("redhand.png", NULL); - - if (!pixbuf) - g_error("pixbuf load failed"); - clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); /* Make a hand */ - hand = clutter_texture_new_from_pixbuf (pixbuf); + hand = clutter_texture_new_from_file ("redhand.png", NULL); + if (!hand) + g_error("pixbuf load failed"); + clutter_actor_set_position (hand, 240, 140); clutter_actor_show (hand); clutter_container_add_actor (CLUTTER_CONTAINER (stage), hand); diff --git a/tests/test-script.json b/tests/test-script.json index ec43d3aab..72979165d 100644 --- a/tests/test-script.json +++ b/tests/test-script.json @@ -36,7 +36,7 @@ { "id" : "red-hand", "type" : "ClutterTexture", - "pixbuf" : "redhand.png", + "filename" : "redhand.png", "x" : 100, "y" : 100, "width" : "20mm", diff --git a/tests/test-shader.c b/tests/test-shader.c index 6c368fd1b..ceb6d0ef4 100644 --- a/tests/test-shader.c +++ b/tests/test-shader.c @@ -194,7 +194,6 @@ main (gint argc, ClutterActor *stage; ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; ClutterShader *shader; - GdkPixbuf *pixbuf; GError *error; clutter_init (&argc, &argv); @@ -222,10 +221,6 @@ main (gint argc, return EXIT_FAILURE; } - pixbuf = gdk_pixbuf_new_from_file ("redhand.png", &error); - if (!pixbuf) - g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); - clutter_stage_set_title (CLUTTER_STAGE (stage), "Shader Test"); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); @@ -234,15 +229,21 @@ main (gint argc, g_object_set (timeline, "loop", TRUE, NULL); /* have it loop */ #ifndef TEST_GROUP - actor = clutter_texture_new_from_pixbuf (pixbuf); + actor = clutter_texture_new_from_file ("redhand.png", &error); + if (!actor) + g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); #else actor = clutter_group_new (); { ClutterActor *child1, *child2, *child3, *child4; ClutterColor color = { 0xff, 0x22, 0x66, 0x99 }; - child1 = clutter_texture_new_from_pixbuf (pixbuf); - child2 = clutter_texture_new_from_pixbuf (pixbuf); + child1 = clutter_texture_new_from_file ("redhand.png", &error); + if (!child1) + g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); + child2 = clutter_texture_new_from_file ("redhand.png", &error); + if (!child2) + g_error("pixbuf load failed: %s", error ? error->message : "Unknown"); child3 = clutter_rectangle_new (); child4 = clutter_label_new_with_text ("Sans 20px", "Shady stuff"); diff --git a/tests/test-stage-read-pixels.c b/tests/test-stage-read-pixels.c new file mode 100644 index 000000000..81a36575b --- /dev/null +++ b/tests/test-stage-read-pixels.c @@ -0,0 +1,282 @@ +#include + +#if defined (_MSC_VER) && !defined (_USE_MATH_DEFINES) +#define _USE_MATH_DEFINES +#endif + +#include +#include +#include +#include + +#define TRAILS 0 +#define NHANDS 6 +#define RADIUS ((CLUTTER_STAGE_WIDTH()+CLUTTER_STAGE_HEIGHT())/NHANDS) + +typedef struct SuperOH +{ + ClutterActor **hand, *bgtex; + ClutterActor *group; + + ClutterActor *stage; + ClutterActor *tv; + +} SuperOH; + +static gint n_hands = NHANDS; + +static GOptionEntry super_oh_entries[] = { + { + "num-hands", 'n', + 0, + G_OPTION_ARG_INT, &n_hands, + "Number of hands", "HANDS" + }, + { NULL } +}; + +static gint +get_radius (void) +{ + return (CLUTTER_STAGE_HEIGHT() + CLUTTER_STAGE_HEIGHT()) / n_hands ; +} + +/* input handler */ +static gboolean +input_cb (ClutterStage *stage, + ClutterEvent *event, + gpointer data) +{ + if (event->type == CLUTTER_BUTTON_PRESS) + { + ClutterButtonEvent *button_event; + ClutterActor *e; + gint x, y; + + clutter_event_get_coords (event, &x, &y); + + button_event = (ClutterButtonEvent *) event; + g_print ("*** button press event (button:%d) ***\n", + button_event->button); + + e = clutter_stage_get_actor_at_pos (stage, x, y); + + if (e && (CLUTTER_IS_TEXTURE (e) || CLUTTER_IS_CLONE_TEXTURE (e))) + { + clutter_actor_hide (e); + return TRUE; + } + } + else if (event->type == CLUTTER_KEY_RELEASE) + { + ClutterKeyEvent *kev = (ClutterKeyEvent *) event; + + g_print ("*** key press event (key:%c) ***\n", + clutter_key_event_symbol (kev)); + + if (clutter_key_event_symbol (kev) == CLUTTER_q) + { + clutter_main_quit (); + return TRUE; + } + } + + return FALSE; +} + +static gboolean update_snapshot (gpointer data) +{ + SuperOH *oh = data; + gint width = CLUTTER_STAGE_WIDTH(); + gint height = CLUTTER_STAGE_HEIGHT(); + guchar *pixels; + + pixels = clutter_stage_read_pixels (CLUTTER_STAGE (oh->stage), 0, 0, width, height); + g_assert (pixels); + + clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (oh->tv), pixels, TRUE, width, height, width *4, + 4, 0, NULL); + g_free (pixels); + return TRUE; +} + +/* Timeline handler */ +static void +frame_cb (ClutterTimeline *timeline, + gint frame_num, + gpointer data) +{ + SuperOH *oh = (SuperOH *)data; + gint i; + + /* Rotate everything clockwise about stage center*/ + + clutter_actor_set_rotation (CLUTTER_ACTOR (oh->group), + CLUTTER_Z_AXIS, + frame_num, + CLUTTER_STAGE_WIDTH () / 2, + CLUTTER_STAGE_HEIGHT () / 2, + 0); + + for (i = 0; i < n_hands; i++) + { + gdouble scale_x, scale_y; + + clutter_actor_get_scale (oh->hand[i], &scale_x, &scale_y); + + /* Rotate each hand around there centers - to get this we need + * to take into account any scaling. + * + * FIXME: scaling causes drift so disabled for now. Need rotation + * unit based functions to fix. + */ + clutter_actor_set_rotation (oh->hand[i], CLUTTER_Z_AXIS, + - 6.0 * frame_num, 0, 0, 0); + } + update_snapshot (oh); +} + + +int +main (int argc, char *argv[]) +{ + ClutterTimeline *timeline; + ClutterAlpha *alpha; + ClutterBehaviour *scaler_1, *scaler_2; + ClutterActor *stage; + ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; + SuperOH *oh; + gint i; + GError *error; + + error = NULL; + + clutter_init_with_args (&argc, &argv, + NULL, + super_oh_entries, + NULL, + &error); + if (error) + { + g_warning ("Unable to initialise Clutter:\n%s", + error->message); + g_error_free (error); + + exit (1); + } + + stage = clutter_stage_get_default (); + clutter_actor_set_size (stage, 800, 600); + + clutter_stage_set_title (CLUTTER_STAGE (stage), "Actors Test"); + clutter_stage_set_color (CLUTTER_STAGE (stage), + &stage_color); + + oh = g_new(SuperOH, 1); + + /* Create a timeline to manage animation */ + timeline = clutter_timeline_new (360, 60); /* num frames, fps */ + g_object_set (timeline, "loop", TRUE, NULL); /* have it loop */ + + /* fire a callback for frame change */ + g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), oh); + + /* Set up some behaviours to handle scaling */ + alpha = clutter_alpha_new_full (timeline, CLUTTER_ALPHA_SINE, NULL, NULL); + + scaler_1 = clutter_behaviour_scale_new (alpha, + 0.5, 0.5, + 1.0, 1.0); + + scaler_2 = clutter_behaviour_scale_new (alpha, + 1.0, 1.0, + 0.5, 0.5); + + /* create a new group to hold multiple actors in a group */ + oh->group = clutter_group_new(); + + oh->hand = g_new (ClutterActor*, n_hands); + for (i = 0; i < n_hands; i++) + { + gint x, y, w, h; + gint radius = get_radius (); + + /* Create a texture from file, then clone in to same resources */ + if (i == 0) + { + if ((oh->hand[i] = clutter_texture_new_from_file ("redhand.png", + &error)) == NULL) + { + g_error ("image load failed: %s", error->message); + exit (1); + } + } + else + oh->hand[i] = clutter_clone_texture_new (CLUTTER_TEXTURE(oh->hand[0])); + + /* Place around a circle */ + w = clutter_actor_get_width (oh->hand[0]); + h = clutter_actor_get_height (oh->hand[0]); + + x = CLUTTER_STAGE_WIDTH () / 2 + + radius + * cos (i * M_PI / (n_hands / 2)) + - w / 2; + + y = CLUTTER_STAGE_HEIGHT () / 2 + + radius + * sin (i * M_PI / (n_hands / 2)) + - h / 2; + + clutter_actor_set_position (oh->hand[i], x, y); + + clutter_actor_move_anchor_point_from_gravity (oh->hand[i], + CLUTTER_GRAVITY_CENTER); + + /* Add to our group group */ + clutter_container_add_actor (CLUTTER_CONTAINER (oh->group), oh->hand[i]); + +#if 1 /* FIXME: disabled as causes drift? - see comment above */ + if (i % 2) + clutter_behaviour_apply (scaler_1, oh->hand[i]); + else + clutter_behaviour_apply (scaler_2, oh->hand[i]); +#endif + } + + /* Add the group to the stage */ + clutter_container_add_actor (CLUTTER_CONTAINER (stage), + CLUTTER_ACTOR (oh->group)); + + /* Show everying ( and map window ) */ + clutter_actor_show (stage); + + oh->stage = stage; + oh->tv = clutter_texture_new (); + + clutter_container_add_actor (CLUTTER_CONTAINER (stage), + CLUTTER_ACTOR (oh->tv)); + clutter_actor_set_position (oh->tv, CLUTTER_STAGE_WIDTH() * 0.025, CLUTTER_STAGE_HEIGHT() * 0.025); + clutter_actor_set_scale (oh->tv, 0.95, 0.95); + clutter_actor_set_opacity (oh->tv, 0.9 * 0xff); + clutter_actor_show (oh->tv); + clutter_actor_lower_bottom (oh->tv); + + g_signal_connect (stage, "button-press-event", + G_CALLBACK (input_cb), + oh); + g_signal_connect (stage, "key-release-event", + G_CALLBACK (input_cb), + oh); + + /* and start it */ + clutter_timeline_start (timeline); + + clutter_main (); + + g_free (oh->tv); + g_free (oh->hand); + g_free (oh); + + return 0; +} diff --git a/tests/test-text.c b/tests/test-text.c index e1db83910..9eeb3ea0e 100644 --- a/tests/test-text.c +++ b/tests/test-text.c @@ -9,6 +9,30 @@ #define COLS 18 #define ROWS 20 +gboolean idle (gpointer data) +{ + ClutterActor *stage = CLUTTER_ACTOR (data); + + static GTimer *timer = NULL; + static int fps = 0; + + if (!timer) + { + timer = g_timer_new (); + g_timer_start (timer); + } + + if (g_timer_elapsed (timer, NULL) >= 1) + { + printf ("fps: %d\n", fps); + g_timer_start (timer); + fps = 0; + } + + clutter_actor_paint (stage); + ++fps; +} + int main (int argc, char *argv[]) { @@ -69,6 +93,8 @@ main (int argc, char *argv[]) g_signal_connect (stage, "key-press-event", G_CALLBACK (clutter_main_quit), NULL); + + g_idle_add (idle, (gpointer) stage); clutter_main(); diff --git a/tests/test-textures.c b/tests/test-textures.c index d655ba880..bfcf8c1dd 100644 --- a/tests/test-textures.c +++ b/tests/test-textures.c @@ -1,5 +1,6 @@ #include +#ifdef USE_PIXBUF GdkPixbuf* make_pixbuf (int width, int height, int bpp, int has_alpha) { @@ -48,14 +49,16 @@ make_pixbuf (int width, int height, int bpp, int has_alpha) #define SPIN() while (g_main_context_pending (NULL)) \ g_main_context_iteration (NULL, FALSE); +#endif int main (int argc, char *argv[]) { +#ifdef USE_PIXBUF ClutterActor *texture; ClutterActor *stage; GdkPixbuf *pixbuf; - gint i, j, cols, rows; + gint i, j; clutter_init (&argc, &argv); @@ -73,15 +76,24 @@ main (int argc, char *argv[]) g_error("%ix%i pixbuf creation failed", i+j, i+j); printf("o %ix%i pixbuf... ", i+j, i+j); - - texture = clutter_texture_new_from_pixbuf (pixbuf); + + texture = clutter_texture_new (); + clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (texture), + gdk_pixbuf_get_pixels (pixbuf), + gdk_pixbuf_get_has_alpha (pixbuf), + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + gdk_pixbuf_get_rowstride (pixbuf), + gdk_pixbuf_get_has_alpha (pixbuf) + ? 4 : 3, + 0, NULL); g_object_unref (pixbuf); if (!texture) g_error("Pixbuf creation failed"); - printf("uploaded to texture... "); + printf("uploaded to texture...\n"); clutter_container_add (CLUTTER_CONTAINER (stage), texture, NULL); clutter_actor_set_size (texture, 400, 400); @@ -91,14 +103,10 @@ main (int argc, char *argv[]) clutter_actor_hide (texture); clutter_actor_show (texture); - clutter_texture_get_n_tiles(CLUTTER_TEXTURE(texture), &cols, &rows); - - printf("with tiles: %i x %i\n", cols, rows); - SPIN(); clutter_container_remove (CLUTTER_CONTAINER (stage), texture, NULL); } - +#endif return 0; } diff --git a/tests/test-viewport.c b/tests/test-viewport.c index 7abaecb3f..0e08981b5 100644 --- a/tests/test-viewport.c +++ b/tests/test-viewport.c @@ -15,22 +15,19 @@ main (int argc, char *argv[]) ClutterActor *stage; ClutterActor *hand; ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xff }; - GdkPixbuf *pixbuf; clutter_init (&argc, &argv); stage = clutter_stage_get_default (); - pixbuf = gdk_pixbuf_new_from_file ("redhand.png", NULL); - - if (!pixbuf) - g_error("pixbuf load failed"); - clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); /* Make a hand */ - hand = clutter_texture_new_from_pixbuf (pixbuf); + hand = clutter_texture_new_from_file ("redhand.png", NULL); + if (!hand) + g_error("pixbuf load failed"); + clutter_actor_set_position (hand, 300, 200); clutter_actor_set_clip (hand, 20, 21, 132, 170); clutter_actor_set_anchor_point (hand, 86, 125);