/* (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) (c) Copyright 2000-2004 Convergence (integrated media) GmbH All rights reserved. Written by Denis Oliver Kropp , Andreas Hundt , Sven Neumann , Ville Syrjälä and Claudio Ciccani . 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. */ #include #include #include #include #include #include #include #include D_DEBUG_DOMAIN( Core_Surface, "Core/Surface", "DirectFB Core Surface" ); /**********************************************************************************************************************/ static const ReactionFunc dfb_surface_globals[] = { /* 0 */ _dfb_layer_region_surface_listener, /* 1 */ _dfb_windowstack_background_image_listener, NULL }; static void surface_destructor( FusionObject *object, bool zombie, void *ctx ) { int i; CoreSurface *surface = (CoreSurface*) object; D_MAGIC_ASSERT( surface, CoreSurface ); D_DEBUG_AT( Core_Surface, "destroying %p (%dx%d%s)\n", surface, surface->config.size.w, surface->config.size.h, zombie ? " ZOMBIE" : ""); dfb_surface_lock( surface ); surface->state |= CSSF_DESTROYED; /* announce surface destruction */ dfb_surface_notify( surface, CSNF_DESTROY ); /* unlink palette */ if (surface->palette) { dfb_palette_detach_global( surface->palette, &surface->palette_reaction ); dfb_palette_unlink( &surface->palette ); } /* destroy buffers */ for (i=0; ibuffers[i]) dfb_surface_buffer_destroy( surface->buffers[i] ); } direct_serial_deinit( &surface->serial ); dfb_surface_unlock( surface ); fusion_skirmish_destroy( &surface->lock ); D_MAGIC_CLEAR( surface ); fusion_object_destroy( object ); } FusionObjectPool * dfb_surface_pool_create( const FusionWorld *world ) { FusionObjectPool *pool; pool = fusion_object_pool_create( "Surface Pool", sizeof(CoreSurface), sizeof(CoreSurfaceNotification), surface_destructor, NULL, world ); return pool; } /**********************************************************************************************************************/ DFBResult dfb_surface_create( CoreDFB *core, const CoreSurfaceConfig *config, CoreSurfaceTypeFlags type, unsigned long resource_id, CorePalette *palette, CoreSurface **ret_surface ) { DFBResult ret = DFB_BUG; int i; int buffers; CoreSurface *surface; char buf[64]; D_ASSERT( core != NULL ); D_FLAGS_ASSERT( type, CSTF_ALL ); D_MAGIC_ASSERT_IF( palette, CorePalette ); D_ASSERT( ret_surface != NULL ); D_DEBUG_AT( Core_Surface, "dfb_surface_create( %p, %p, %p )\n", core, config, ret_surface ); surface = dfb_core_create_surface( core ); if (!surface) return DFB_FUSION; if (config) { D_FLAGS_ASSERT( config->flags, CSCONF_ALL ); surface->config.flags = config->flags; if (config->flags & CSCONF_SIZE) { D_DEBUG_AT( Core_Surface, " -> %dx%d\n", config->size.w, config->size.h ); surface->config.size = config->size; } if (config->flags & CSCONF_FORMAT) { D_DEBUG_AT( Core_Surface, " -> %s\n", dfb_pixelformat_name( config->format ) ); surface->config.format = config->format; } if (config->flags & CSCONF_CAPS) { D_DEBUG_AT( Core_Surface, " -> caps 0x%08x\n", config->caps ); if (config->caps & DSCAPS_ROTATED) D_UNIMPLEMENTED(); surface->config.caps = config->caps & ~DSCAPS_ROTATED; } if (config->flags & CSCONF_PREALLOCATED) { D_DEBUG_AT( Core_Surface, " -> prealloc %p [%d]\n", config->preallocated[0].addr, config->preallocated[0].pitch ); direct_memcpy( surface->config.preallocated, config->preallocated, sizeof(config->preallocated) ); type |= CSTF_PREALLOCATED; } } if (surface->config.caps & DSCAPS_SYSTEMONLY) surface->type = (type & ~CSTF_EXTERNAL) | CSTF_INTERNAL; else if (surface->config.caps & DSCAPS_VIDEOONLY) surface->type = (type & ~CSTF_INTERNAL) | CSTF_EXTERNAL; else surface->type = type & ~(CSTF_INTERNAL | CSTF_EXTERNAL); if (surface->config.caps & DSCAPS_SHARED) surface->type |= CSTF_SHARED; surface->resource_id = resource_id; if (surface->config.caps & DSCAPS_TRIPLE) buffers = 3; else if (surface->config.caps & DSCAPS_DOUBLE) buffers = 2; else { buffers = 1; surface->config.caps &= ~DSCAPS_ROTATED; } surface->notifications = CSNF_ALL & ~CSNF_FLIP; surface->alpha_ramp[0] = 0x00; surface->alpha_ramp[1] = 0x55; surface->alpha_ramp[2] = 0xaa; surface->alpha_ramp[3] = 0xff; if (surface->config.caps & DSCAPS_STATIC_ALLOC) surface->config.min_size = surface->config.size; surface->shmpool = dfb_core_shmpool( core ); direct_serial_init( &surface->serial ); snprintf( buf, sizeof(buf), "Surface %dx%d %s", surface->config.size.w, surface->config.size.h, dfb_pixelformat_name(surface->config.format) ); fusion_ref_set_name( &surface->object.ref, buf ); fusion_skirmish_init( &surface->lock, buf, dfb_core_world(core) ); fusion_object_set_lock( &surface->object, &surface->lock ); D_MAGIC_SET( surface, CoreSurface ); if (dfb_config->warn.flags & DCWF_CREATE_SURFACE && dfb_config->warn.create_surface.min_size.w <= surface->config.size.w && dfb_config->warn.create_surface.min_size.h <= surface->config.size.h) D_WARN( "create-surface %4dx%4d %6s, buffers %d, caps 0x%08x, type 0x%08x", surface->config.size.w, surface->config.size.h, dfb_pixelformat_name(surface->config.format), buffers, surface->config.caps, surface->type ); if (palette) { dfb_surface_set_palette( surface, palette ); } else if (DFB_PIXELFORMAT_IS_INDEXED( surface->config.format )) { ret = dfb_surface_init_palette( core, surface ); if (ret) goto error; } /* Create the Surface Buffers. */ for (i=0; ibuffers[surface->num_buffers++] = buffer; switch (i) { case 0: surface->buffer_indices[CSBR_FRONT] = i; case 1: surface->buffer_indices[CSBR_BACK] = i; case 2: surface->buffer_indices[CSBR_IDLE] = i; } } fusion_object_activate( &surface->object ); *ret_surface = surface; return DFB_OK; error: D_MAGIC_CLEAR( surface ); for (i=0; ibuffers[i]) dfb_surface_buffer_destroy( surface->buffers[i] ); } fusion_skirmish_destroy( &surface->lock ); direct_serial_deinit( &surface->serial ); fusion_object_destroy( &surface->object ); return ret; } DFBResult dfb_surface_create_simple ( CoreDFB *core, int width, int height, DFBSurfacePixelFormat format, DFBSurfaceCapabilities caps, CoreSurfaceTypeFlags type, unsigned long resource_id, CorePalette *palette, CoreSurface **ret_surface ) { CoreSurfaceConfig config; D_DEBUG_AT( Core_Surface, "%s( %p, %dx%d %s, %p )\n", __FUNCTION__, core, width, height, dfb_pixelformat_name( format ), ret_surface ); D_ASSERT( core != NULL ); D_ASSERT( ret_surface != NULL ); config.flags = CSCONF_SIZE | CSCONF_FORMAT | CSCONF_CAPS; config.size.w = width; config.size.h = height; config.format = format; config.caps = caps; return dfb_surface_create( core, &config, type, resource_id, palette, ret_surface ); } DFBResult dfb_surface_init_palette( CoreDFB *core, CoreSurface *surface ) { DFBResult ret; CorePalette *palette; ret = dfb_palette_create( core, 1 << DFB_COLOR_BITS_PER_PIXEL( surface->config.format ), &palette ); if (ret) { D_DERROR( ret, "Core/Surface: Error creating palette!\n" ); return ret; } switch (surface->config.format) { case DSPF_LUT8: dfb_palette_generate_rgb332_map( palette ); break; case DSPF_ALUT44: dfb_palette_generate_rgb121_map( palette ); break; default: break; } dfb_surface_set_palette( surface, palette ); dfb_palette_unref( palette ); return DFB_OK; } DFBResult dfb_surface_notify( CoreSurface *surface, CoreSurfaceNotificationFlags flags) { CoreSurfaceNotification notification; D_MAGIC_ASSERT( surface, CoreSurface ); FUSION_SKIRMISH_ASSERT( &surface->lock ); D_FLAGS_ASSERT( flags, CSNF_ALL ); direct_serial_increase( &surface->serial ); if (!(surface->state & CSSF_DESTROYED)) { if (!(surface->notifications & flags)) return DFB_OK; } notification.flags = flags; notification.surface = surface; return dfb_surface_dispatch( surface, ¬ification, dfb_surface_globals ); } DFBResult dfb_surface_flip( CoreSurface *surface, bool swap ) { unsigned int back, front; D_MAGIC_ASSERT( surface, CoreSurface ); FUSION_SKIRMISH_ASSERT( &surface->lock ); back = (surface->flips + CSBR_BACK) % surface->num_buffers; front = (surface->flips + CSBR_FRONT) % surface->num_buffers; D_ASSERT( surface->buffer_indices[back] < surface->num_buffers ); D_ASSERT( surface->buffer_indices[front] < surface->num_buffers ); if (surface->buffers[surface->buffer_indices[back]]->policy != surface->buffers[surface->buffer_indices[front]]->policy || (surface->config.caps & DSCAPS_ROTATED)) return DFB_UNSUPPORTED; if (swap) { int tmp = surface->buffer_indices[back]; surface->buffer_indices[back] = surface->buffer_indices[front]; surface->buffer_indices[front] = tmp; } else surface->flips++; dfb_surface_notify( surface, CSNF_FLIP ); return DFB_OK; } DFBResult dfb_surface_reconfig( CoreSurface *surface, const CoreSurfaceConfig *config ) { int i, buffers; DFBResult ret; D_DEBUG_AT( Core_Surface, "%s( %p, %dx%d %s -> %dx%d %s )\n", __FUNCTION__, surface, surface->config.size.w, surface->config.size.h, dfb_pixelformat_name( surface->config.format ), (config->flags & CSCONF_SIZE) ? config->size.w : surface->config.size.w, (config->flags & CSCONF_SIZE) ? config->size.h : surface->config.size.h, (config->flags & CSCONF_FORMAT) ? dfb_pixelformat_name( config->format ) : dfb_pixelformat_name( surface->config.format ) ); D_MAGIC_ASSERT( surface, CoreSurface ); D_ASSERT( config != NULL ); if (surface->type & CSTF_PREALLOCATED) return DFB_UNSUPPORTED; if (config->flags & CSCONF_PREALLOCATED) return DFB_UNSUPPORTED; if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; if ( (config->flags == CSCONF_SIZE || ((config->flags == (CSCONF_SIZE | CSCONF_FORMAT)) && (config->format == surface->config.format))) && config->size.w <= surface->config.min_size.w && config->size.h <= surface->config.min_size.h) { surface->config.size = config->size; fusion_skirmish_dismiss( &surface->lock ); return DFB_OK; } #if 1 /* Precheck the Surface Buffers. */ for (i=0; inum_buffers; i++) { if (surface->buffers[i]->locked) { D_DEBUG_AT( Core_Surface, " -> surface is locked, cannot reconfigure!\n" ); fusion_skirmish_dismiss( &surface->lock ); return DFB_LOCKED; } } #endif /* Destroy the Surface Buffers. */ for (i=0; inum_buffers; i++) { dfb_surface_buffer_destroy( surface->buffers[i] ); surface->buffers[i] = NULL; } surface->num_buffers = 0; if (config->flags & CSCONF_SIZE) surface->config.size = config->size; if (config->flags & CSCONF_FORMAT) surface->config.format = config->format; if (config->flags & CSCONF_CAPS) { if (config->caps & DSCAPS_ROTATED) D_UNIMPLEMENTED(); surface->config.caps = config->caps & ~DSCAPS_ROTATED; } if (surface->config.caps & DSCAPS_SYSTEMONLY) surface->type = (surface->type & ~CSTF_EXTERNAL) | CSTF_INTERNAL; else if (surface->config.caps & DSCAPS_VIDEOONLY) surface->type = (surface->type & ~CSTF_INTERNAL) | CSTF_EXTERNAL; else surface->type = surface->type & ~(CSTF_INTERNAL | CSTF_EXTERNAL); if (surface->config.caps & DSCAPS_TRIPLE) buffers = 3; else if (surface->config.caps & DSCAPS_DOUBLE) buffers = 2; else { buffers = 1; surface->config.caps &= ~DSCAPS_ROTATED; } /* Recreate the Surface Buffers. */ for (i=0; ibuffers[surface->num_buffers++] = buffer; switch (i) { case 0: surface->buffer_indices[CSBR_FRONT] = i; case 1: surface->buffer_indices[CSBR_BACK] = i; case 2: surface->buffer_indices[CSBR_IDLE] = i; } } dfb_surface_notify( surface, CSNF_SIZEFORMAT ); fusion_skirmish_dismiss( &surface->lock ); return DFB_OK; error: D_UNIMPLEMENTED(); fusion_skirmish_dismiss( &surface->lock ); return ret; } DFBResult dfb_surface_destroy_buffers( CoreSurface *surface ) { int i; D_MAGIC_ASSERT( surface, CoreSurface ); if (surface->type & CSTF_PREALLOCATED) return DFB_UNSUPPORTED; if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; /* Destroy the Surface Buffers. */ for (i=0; inum_buffers; i++) { dfb_surface_buffer_destroy( surface->buffers[i] ); surface->buffers[i] = NULL; } surface->num_buffers = 0; fusion_skirmish_dismiss( &surface->lock ); return DFB_OK; } DFBResult dfb_surface_lock_buffer( CoreSurface *surface, CoreSurfaceBufferRole role, CoreSurfaceAccessorID accessor, CoreSurfaceAccessFlags access, CoreSurfaceBufferLock *ret_lock ) { DFBResult ret; CoreSurfaceBuffer *buffer; D_MAGIC_ASSERT( surface, CoreSurface ); if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; buffer = dfb_surface_get_buffer( surface, role ); D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); ret = dfb_surface_buffer_lock( buffer, accessor, access, ret_lock ); fusion_skirmish_dismiss( &surface->lock ); return ret; } DFBResult dfb_surface_unlock_buffer( CoreSurface *surface, CoreSurfaceBufferLock *lock ) { DFBResult ret; D_MAGIC_ASSERT( surface, CoreSurface ); if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; ret = dfb_surface_buffer_unlock( lock ); fusion_skirmish_dismiss( &surface->lock ); return ret; } DFBResult dfb_surface_read_buffer( CoreSurface *surface, CoreSurfaceBufferRole role, void *destination, int pitch, const DFBRectangle *rect ) { DFBResult ret; CoreSurfaceBuffer *buffer; D_MAGIC_ASSERT( surface, CoreSurface ); D_ASSERT( destination != NULL ); D_ASSERT( pitch > 0 ); DFB_RECTANGLE_ASSERT_IF( rect ); if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; buffer = dfb_surface_get_buffer( surface, role ); D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); ret = dfb_surface_buffer_read( buffer, destination, pitch, rect ); fusion_skirmish_dismiss( &surface->lock ); return ret; } DFBResult dfb_surface_write_buffer( CoreSurface *surface, CoreSurfaceBufferRole role, const void *source, int pitch, const DFBRectangle *rect ) { DFBResult ret; CoreSurfaceBuffer *buffer; D_MAGIC_ASSERT( surface, CoreSurface ); D_ASSERT( source != NULL ); D_ASSERT( pitch > 0 ); DFB_RECTANGLE_ASSERT_IF( rect ); if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; buffer = dfb_surface_get_buffer( surface, role ); D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); ret = dfb_surface_buffer_write( buffer, source, pitch, rect ); fusion_skirmish_dismiss( &surface->lock ); return ret; } DFBResult dfb_surface_dump_buffer( CoreSurface *surface, CoreSurfaceBufferRole role, const char *path, const char *prefix ) { DFBResult ret; CoreSurfaceBuffer *buffer; D_MAGIC_ASSERT( surface, CoreSurface ); D_ASSERT( path != NULL ); D_ASSERT( prefix != NULL ); if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; buffer = dfb_surface_get_buffer( surface, role ); D_MAGIC_ASSERT( buffer, CoreSurfaceBuffer ); ret = dfb_surface_buffer_dump( buffer, path, prefix ); fusion_skirmish_dismiss( &surface->lock ); return ret; } DFBResult dfb_surface_set_palette( CoreSurface *surface, CorePalette *palette ) { D_MAGIC_ASSERT( surface, CoreSurface ); D_MAGIC_ASSERT_IF( palette, CorePalette ); if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; if (surface->palette != palette) { if (surface->palette) { dfb_palette_detach_global( surface->palette, &surface->palette_reaction ); dfb_palette_unlink( &surface->palette ); } if (palette) { dfb_palette_link( &surface->palette, palette ); dfb_palette_attach_global( palette, DFB_SURFACE_PALETTE_LISTENER, surface, &surface->palette_reaction ); } dfb_surface_notify( surface, CSNF_PALETTE_CHANGE ); } fusion_skirmish_dismiss( &surface->lock ); return DFB_OK; } DFBResult dfb_surface_set_field( CoreSurface *surface, int field ) { D_MAGIC_ASSERT( surface, CoreSurface ); if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; surface->field = field; dfb_surface_notify( surface, CSNF_FIELD ); fusion_skirmish_dismiss( &surface->lock ); return DFB_OK; } DFBResult dfb_surface_set_alpha_ramp( CoreSurface *surface, u8 a0, u8 a1, u8 a2, u8 a3 ) { D_MAGIC_ASSERT( surface, CoreSurface ); if (fusion_skirmish_prevail( &surface->lock )) return DFB_FUSION; surface->alpha_ramp[0] = a0; surface->alpha_ramp[1] = a1; surface->alpha_ramp[2] = a2; surface->alpha_ramp[3] = a3; dfb_surface_notify( surface, CSNF_ALPHA_RAMP ); fusion_skirmish_dismiss( &surface->lock ); return DFB_OK; } ReactionResult _dfb_surface_palette_listener( const void *msg_data, void *ctx ) { const CorePaletteNotification *notification = msg_data; CoreSurface *surface = ctx; if (notification->flags & CPNF_DESTROY) return RS_REMOVE; if (notification->flags & CPNF_ENTRIES) { if (fusion_skirmish_prevail( &surface->lock )) return RS_OK; dfb_surface_notify( surface, CSNF_PALETTE_UPDATE ); fusion_skirmish_dismiss( &surface->lock ); } return RS_OK; }