void MCCard::layer_setviewport(int32_t p_x, int32_t p_y, int32_t p_width, int32_t p_height) { MCTileCacheRef t_tilecache; t_tilecache = getstack() -> gettilecache(); // Notify any tilecache of the changes. if (t_tilecache != nil) MCTileCacheSetViewport(t_tilecache, MCU_make_rect(p_x, p_y, p_width, p_height)); // Get the current rect, before updating it. MCRectangle t_old_rect; t_old_rect = rect; // Update the rect. resize(p_width, p_height); // Add the rects to the update region. // MW-2012-05-01: [[ Bug 10157 ]] If the card has a border then add the whole card // rect to the update region; otherwise just add the exposed rects. if (!getflag(F_SHOW_BORDER)) { if (p_width > t_old_rect.width) layer_dirtyrect(MCU_make_rect(t_old_rect.width, 0, p_width - t_old_rect.width, p_height)); if (p_height > t_old_rect.height) layer_dirtyrect(MCU_make_rect(0, t_old_rect.height, p_width, p_height - t_old_rect.height)); } else layer_dirtyrect(rect); }
// MW-2011-10-17: [[ Bug 9813 ]] We need the effective rect before visibility is // changed, else focus border might be not included in our calculation. void MCControl::layer_visibilitychanged(const MCRectangle& p_old_effective_rect) { if (!opened) return; if (!parent -> isvisible() && !MCshowinvisibles) return; // If the control is currently visible, then its old rect must have been // empty; otherwise the old rect is its effectiverect. layer_changeeffectiverect(getflag(F_VISIBLE) ? MCU_make_rect(0, 0, 0, 0) : p_old_effective_rect, false, true); }
bool MCStack::view_snapshottilecache(const MCRectangle &p_stack_rect, MCGImageRef &r_image) { if (m_view_tilecache == nil) return false; // MW-2013-10-29: [[ Bug 11330 ]] Transform stack to (local) view co-ords. MCRectangle t_view_rect; t_view_rect = MCRectangleGetTransformedBounds(p_stack_rect, getviewtransform()); t_view_rect = MCU_intersect_rect(t_view_rect, MCU_make_rect(0, 0, view_getrect() . width, view_getrect() . height)); // IM-2014-01-24: [[ HiDPI ]] use backing surface coords for tilecache operations MCRectangle t_device_rect; t_device_rect = MCRectangleGetScaledBounds(t_view_rect, view_getbackingscale()); return MCTileCacheSnapshot(m_view_tilecache, t_device_rect, r_image); }
bool LockPixels(MCRegionRef p_area, MCGRaster &r_raster) { m_locked_bits = m_raster.pixels; m_locked_stride = m_raster.stride; m_locked_area = MCRegionGetBoundingBox(p_area); // restrict locked area to intersection with raster m_locked_area = MCU_intersect_rect(m_locked_area, MCU_make_rect(0, 0, m_raster.width, m_raster.height)); /* UNCHECKED */ MCRegionIncludeRect(m_redraw_region, m_locked_area); r_raster.width = m_locked_area.width; r_raster.height = m_locked_area.height; r_raster.pixels = (uint8_t *)m_locked_bits + m_locked_area . y * m_locked_stride + m_locked_area . x * sizeof(uint32_t); r_raster.stride = m_locked_stride; r_raster.format = m_raster.format; return true; }
void MCCard::render(void) { MCTileCacheRef t_tiler; t_tiler = getstack() -> gettilecache(); bool t_reset_ids; t_reset_ids = MCTileCacheIsClean(t_tiler); if (getstate(CS_SIZE)) { MCTileCacheLayer t_fg_layer; t_fg_layer . id = m_fg_layer_id; t_fg_layer . region = selrect; t_fg_layer . is_opaque = false; t_fg_layer . opacity = 255; t_fg_layer . ink = GXblendSrcOver; t_fg_layer . callback = render_foreground; t_fg_layer . context = this; MCTileCacheRenderScenery(t_tiler, t_fg_layer); m_fg_layer_id = t_fg_layer . id; } else m_fg_layer_id = 0; MCObjptr *t_objptrs; t_objptrs = getobjptrs(); if (t_objptrs != nil) { MCObjptr *t_objptr; t_objptr = t_objptrs -> prev(); do { MCControl *t_control; t_control = t_objptr -> getref(); // If the tilecache is 'clean' then we must reset the attrs to // force a sync. if (t_reset_ids) t_control -> layer_resetattrs(); // Take note of whether the spriteness of a layer has changed. bool t_old_is_sprite; t_old_is_sprite = t_control -> layer_issprite(); // Sync the attributes, make sure we commit the new values. t_control -> layer_computeattrs(true); // Initialize the common layer props. MCTileCacheLayer t_layer; t_layer . id = t_control -> layer_getid(); t_layer . opacity = t_control -> getopacity(); t_layer . ink = t_control -> getink(); t_layer . context = t_control; // The opaqueness of a layer has already been computed. t_layer . is_opaque = t_control -> layer_isopaque(); // Now compute the layer's region/clip. if (!t_control -> getflag(F_VISIBLE) && !MCshowinvisibles) { // Invisible layers just have empty region/clip. t_layer . region = MCU_make_rect(0, 0, 0, 0); t_layer . clip = MCU_make_rect(0, 0, 0, 0); } else if (!t_control -> layer_isscrolling()) { // Non-scrolling layer's are the size of their effective rects. t_layer . region = t_control -> geteffectiverect(); t_layer . clip = t_layer . region; } else { // For a scrolling layer, the clip is the bounds of the control, while // the region we draw is the group's minrect. t_layer . region = t_control -> layer_getcontentrect(); t_layer . clip = t_control -> geteffectiverect(); } // Now render the layer - what method we use depends on whether the // layer is a sprite or not. if (t_control -> layer_issprite()) { // If the layer was not a sprite before, remove the scenery // layer that it was. if (!t_old_is_sprite && t_layer . id != 0) { MCTileCacheRemoveScenery(t_tiler, t_layer . id, t_control -> geteffectiverect()); t_layer . id = 0; } t_layer . callback = testtilecache_sprite_renderer; MCTileCacheRenderSprite(t_tiler, t_layer); } else { // If the layer was a sprite before, remove the sprite // layer that it was. if (t_old_is_sprite && t_layer . id != 0) { MCTileCacheRemoveSprite(t_tiler, t_layer . id); t_layer . id = 0; } t_layer . callback = testtilecache_scenery_renderer; MCTileCacheRenderScenery(t_tiler, t_layer); } // Upate the id. t_control -> layer_setid(t_layer . id); // Advance to the object below. t_objptr = t_objptr -> prev(); } while(t_objptr != t_objptrs -> prev()); } // Final step is to render the background. Note that the background layer // really only needs to be the rect rounded outward to the nearest tile // boundaries, but 8192, 8192 is bigger than it can ever be at present so // is an easier alternative. MCTileCacheLayer t_bg_layer; t_bg_layer . id = m_bg_layer_id; t_bg_layer . region = MCU_make_rect(0, 0, 8192, 8192); t_bg_layer . is_opaque = true; t_bg_layer . opacity = 255; t_bg_layer . ink = GXblendSrcOver; t_bg_layer . callback = render_background; t_bg_layer . context = this; MCTileCacheRenderScenery(t_tiler, t_bg_layer); m_bg_layer_id = t_bg_layer . id; }
void MCControl::layer_changeeffectiverect(const MCRectangle& p_old_effective_rect, bool p_force_update, bool p_update_card) { // Compute the 'new' effectiverect based on visibility. MCRectangle t_new_effective_rect; if (getflag(F_VISIBLE) || MCshowinvisibles) t_new_effective_rect = geteffectiverect(); else MCU_set_rect(t_new_effective_rect, 0, 0, 0, 0); // If the effective rect has not changed this is at most an update. if (MCU_equal_rect(p_old_effective_rect, t_new_effective_rect)) { // If we are forcing an update, use the dirty method. if (p_force_update) { // If the layer is not scrolling just defer to the normal // dirty method; otherwise use the dirty content method. if (!layer_isscrolling()) layer_dirtyeffectiverect(t_new_effective_rect, p_update_card); else layer_dirtycontentrect(layer_getcontentrect(), p_update_card); } // We are done. return; } // Fetch the tilecache, making it nil if the parent is a group (in the // latter case, this is just a dirty op). MCTileCacheRef t_tilecache; if (parent -> gettype() != CT_GROUP) t_tilecache = getstack() -> gettilecache(); else t_tilecache = nil; // If no tilecache, then just dirty the old and new effective rects. if (t_tilecache == nil) { layer_dirtyeffectiverect(p_old_effective_rect, p_update_card); layer_dirtyeffectiverect(t_new_effective_rect, p_update_card); return; } // MW-2011-10-17: [[ Bug 9808 ]] Make sure we update the card regardless of // whether we have a layer id - otherwise new objects don't show! // Add the rects to the update region - but only if instructed (update_card will be // false if the object was invisible). if (p_update_card) { static_cast<MCCard *>(parent) -> layer_dirtyrect(p_old_effective_rect); static_cast<MCCard *>(parent) -> layer_dirtyrect(t_new_effective_rect); } // We must be in tile-cache mode with a top-level control, but if the layer // id is zero, there is nothing to do. if (m_layer_id == 0) return; if (!layer_issprite()) { // Non-dynamic layers are scenery in the tilecache, and we must use old // new effective rects so that the appropriate tiles get flushed. Note // that 'force_update' has no effect here as reshaping a scenery layer // implicitly invalidates all tiles it touches. MCTileCacheReshapeScenery(t_tilecache, m_layer_id, p_old_effective_rect, t_new_effective_rect); } else { // Dynamic layers are sprites in the tilecache, and there is nothing to // do unless 'force update' is required. In particular, if the layer is // just moving then no redraw of the layer will be needed. Note, however, // that this implicitly assumes that 'force update' is true if the content // in a sprite-relative co-ord system has changed. if (p_force_update) { MCRectangle t_rect; // If the layer is not scrolling, just use the width/height from the // effective rect; otherwise use content width/height. if (!layer_isscrolling()) t_rect = p_old_effective_rect; else t_rect = layer_getcontentrect(); MCTileCacheUpdateSprite(t_tilecache, m_layer_id, MCU_make_rect(0, 0, t_rect . width, t_rect . height)); } } }
void MCStack::effectrect(const MCRectangle& p_area, Boolean& r_abort) { // Get the list of effects. MCEffectList *t_effects = MCcur_effects; MCcur_effects = NULL; // If the window isn't opened or hasn't been attached (plugin) or if we have no // snapshot to use, this is a no-op. if (!opened || !haswindow() || m_snapshot == nil) { while(t_effects != NULL) { MCEffectList *t_effect; t_effect = t_effects; t_effects = t_effects -> next; delete t_effect; } return; } // Mark the stack as being in an effect. state |= CS_EFFECT; // Lock messages while the effect is happening. Boolean t_old_lockmessages; t_old_lockmessages = MClockmessages; MClockmessages = True; // Calculate the area of interest. MCRectangle t_effect_area; t_effect_area = curcard -> getrect(); t_effect_area . y = getscroll(); t_effect_area . height -= t_effect_area . y; t_effect_area = MCU_intersect_rect(t_effect_area, p_area); // IM-2013-08-21: [[ ResIndependence ]] Scale effect area to device coords // Align snapshot rect to device pixels // IM-2013-09-30: [[ FullscreenMode ]] Use stack transform to get device coords MCGAffineTransform t_transform; t_transform = getdevicetransform(); // MW-2013-10-29: [[ Bug 11330 ]] Make sure the effect area is cropped to the visible // area. t_effect_area = MCRectangleGetTransformedBounds(t_effect_area, getviewtransform()); t_effect_area = MCU_intersect_rect(t_effect_area, MCU_make_rect(0, 0, view_getrect() . width, view_getrect() . height)); // IM-2014-01-24: [[ HiDPI ]] scale effect region to backing surface coords MCGFloat t_scale; t_scale = view_getbackingscale(); MCRectangle t_device_rect, t_user_rect; t_device_rect = MCRectangleGetScaledBounds(t_effect_area, t_scale); t_user_rect = MCRectangleGetTransformedBounds(t_device_rect, MCGAffineTransformInvert(t_transform)); // IM-2013-08-29: [[ RefactorGraphics ]] get device height for CoreImage effects // IM-2013-09-30: [[ FullscreenMode ]] Use view rect to get device height uint32_t t_device_height; t_device_height = floor(view_getrect().height * t_scale); // Make a region of the effect area // IM-2013-08-29: [[ ResIndependence ]] scale effect region to device coords MCRegionRef t_effect_region; t_effect_region = nil; /* UNCHECKED */ MCRegionCreate(t_effect_region); /* UNCHECKED */ MCRegionSetRect(t_effect_region, t_effect_area); #ifndef FEATURE_PLATFORM_PLAYER #if defined(FEATURE_QUICKTIME) // MW-2010-07-07: Make sure QT is only loaded if we actually are doing an effect if (t_effects != nil) if (!MCdontuseQTeffects) if (!MCtemplateplayer -> isQTinitted()) MCtemplateplayer -> initqt(); #endif #endif // Lock the screen to prevent any updates occuring until we want them. MCRedrawLockScreen(); // By default, we have not aborted. r_abort = False; MCGImageRef t_initial_image; t_initial_image = MCGImageRetain(m_snapshot); while(t_effects != nil) { uint32_t t_duration; t_duration = MCU_max(1, MCeffectrate / (t_effects -> speed - VE_VERY)); if (t_effects -> type == VE_DISSOLVE) t_duration *= 2; uint32_t t_delta; t_delta = 0; // Create surface at effect_area size. // Render into surface based on t_effects -> image MCGImageRef t_final_image = nil; // If this isn't a plain effect, then we must fetch first and last images. if (t_effects -> type != VE_PLAIN) { // Render the final image. MCGContextRef t_context = nil; // IM-2014-05-20: [[ GraphicsPerformance ]] Create opaque context for snapshot /* UNCHECKED */ MCGContextCreate(t_device_rect.width, t_device_rect.height, false, t_context); MCGContextTranslateCTM(t_context, -t_device_rect.x, -t_device_rect.y); // IM-2013-10-03: [[ FullscreenMode ]] Apply device transform to context MCGContextConcatCTM(t_context, t_transform); // Configure the context. MCGContextClipToRect(t_context, MCRectangleToMCGRectangle(t_user_rect)); // Render an appropriate image switch(t_effects -> image) { case VE_INVERSE: { MCContext *t_old_context = nil; /* UNCHECKED */ t_old_context = new MCGraphicsContext(t_context); curcard->draw(t_old_context, t_user_rect, false); delete t_old_context; MCGContextSetFillRGBAColor(t_context, 1.0, 1.0, 1.0, 1.0); MCGContextSetBlendMode(t_context, kMCGBlendModeDifference); MCGContextAddRectangle(t_context, MCRectangleToMCGRectangle(t_user_rect)); MCGContextFill(t_context); } break; case VE_BLACK: MCGContextSetFillRGBAColor(t_context, 0.0, 0.0, 0.0, 1.0); MCGContextAddRectangle(t_context, MCRectangleToMCGRectangle(t_user_rect)); MCGContextFill(t_context); break; case VE_WHITE: MCGContextSetFillRGBAColor(t_context, 1.0, 1.0, 1.0, 1.0); MCGContextAddRectangle(t_context, MCRectangleToMCGRectangle(t_user_rect)); MCGContextFill(t_context); break; case VE_GRAY: MCGContextSetFillRGBAColor(t_context, 0.5, 0.5, 0.5, 1.0); MCGContextAddRectangle(t_context, MCRectangleToMCGRectangle(t_user_rect)); MCGContextFill(t_context); break; default: { MCContext *t_old_context = nil; /* UNCHECKED */ t_old_context = new MCGraphicsContext(t_context); curcard->draw(t_old_context, t_user_rect, false); delete t_old_context; } } /* UNCHECKED */ MCGContextCopyImage(t_context, t_final_image); MCGContextRelease(t_context); } MCStackEffectContext t_context; t_context.delta = t_delta; t_context.duration = t_duration; t_context.effect = t_effects; t_context.effect_area = t_device_rect; t_context.initial_image = t_initial_image; t_context.final_image = t_final_image; // MW-2011-10-20: [[ Bug 9824 ]] Make sure dst point is correct. // Initialize the destination with the start image. view_platform_updatewindowwithcallback(t_effect_region, MCStackRenderInitial, &t_context); // If there is a sound, then start playing it. if (t_effects -> sound != NULL) { MCAudioClip *acptr; MCNewAutoNameRef t_sound; /* UNCHECKED */ MCNameCreate(t_effects->sound, &t_sound); if ((acptr = (MCAudioClip *)getobjname(CT_AUDIO_CLIP, *t_sound)) == NULL) { IO_handle stream; if ((stream = MCS_open(t_effects->sound, kMCOpenFileModeRead, True, False, 0)) != NULL) { acptr = new MCAudioClip; acptr->setdisposable(); if (!acptr->import(t_effects->sound, stream)) { delete acptr; acptr = NULL; } MCS_close(stream); } } if (acptr != NULL) { MCU_play_stop(); MCacptr = acptr; MCU_play(); #ifndef FEATURE_PLATFORM_AUDIO if (MCacptr != NULL) MCscreen->addtimer(MCacptr, MCM_internal, PLAY_RATE); #endif } if (MCscreen->wait((real8)MCsyncrate / 1000.0, False, True)) { r_abort = True; break; } } // Initialize CoreImage of QTEffects if needed. if (t_effects -> type != VE_PLAIN) { MCAutoPointer<char> t_name; /* UNCHECKED */ MCStringConvertToCString(t_effects -> name, &t_name); #ifdef _MAC_DESKTOP // IM-2013-08-29: [[ ResIndependence ]] use scaled effect rect for CI effects if (t_effects -> type == VE_UNDEFINED && MCCoreImageEffectBegin(*t_name, t_initial_image, t_final_image, t_device_rect, t_device_height, t_effects -> arguments)) t_effects -> type = VE_CIEFFECT; else #endif #ifdef FEATURE_QUICKTIME_EFFECTS // IM-2013-08-29: [[ ResIndependence ]] use scaled effect rect for QT effects if (t_effects -> type == VE_UNDEFINED && MCQTEffectBegin(t_effects -> type, *t_name, t_effects -> direction, t_initial_image, t_final_image, t_device_rect)) t_effects -> type = VE_QTEFFECT; #else ; #endif } // Run effect // Now perform the effect loop, but only if there is something to do. if (t_effects -> type != VE_PLAIN || old_blendlevel != blendlevel) { // Calculate timing parameters. double t_start_time; t_start_time = 0.0; for(;;) { t_context.delta = t_delta; Boolean t_drawn = False; view_platform_updatewindowwithcallback(t_effect_region, MCStackRenderEffect, &t_context); // Now redraw the window with the new image. // if (t_drawn) { MCscreen -> sync(getw()); } // Update the window's blendlevel (if needed) if (old_blendlevel != blendlevel) { float t_fraction = float(t_delta) / t_duration; setopacity(uint1((old_blendlevel * 255 + (float(blendlevel) - old_blendlevel) * 255 * t_fraction) / 100)); } // If the start time is zero, then start counting from here. if (t_start_time == 0.0) t_start_time = MCS_time(); // If we've reached the end of the transition, we are done. if (t_delta == t_duration) { #ifdef _ANDROID_MOBILE // MW-2011-12-12: [[ Bug 9907 ]] Make sure we let the screen sync at this point MCscreen -> wait(0.01, False, False); #endif break; } // Get the time now. double t_now; t_now = MCS_time(); // Compute the new delta value. uint32_t t_new_delta; t_new_delta = (uint32_t)ceil((t_now - t_start_time) * 1000.0); // If the new value is same as the old, then advance one step. if (t_new_delta == t_delta) t_delta = t_new_delta + 1; else t_delta = t_new_delta; // If the new delta is beyond the end point, set it to the end. if (t_delta > t_duration) t_delta = t_duration; // Wait until the next boundary, making sure we break for no reason // other than abort. if (MCscreen -> wait((t_start_time + (t_delta / 1000.0)) - t_now, False, False)) r_abort = True; // If we aborted, we render the final step and are thus done. if (r_abort) t_delta = t_duration; } } #ifdef _MAC_DESKTOP if (t_effects -> type == VE_CIEFFECT) MCCoreImageEffectEnd(); else #endif #ifdef FEATURE_QUICKTIME_EFFECTS if (t_effects -> type == VE_QTEFFECT) MCQTEffectEnd(); #endif // Free initial surface. MCGImageRelease(t_initial_image); // initial surface becomes final surface. t_initial_image = t_final_image; t_final_image = nil; // Move to the next effect. MCEffectList *t_current_effect; t_current_effect = t_effects; t_effects = t_effects -> next; delete t_current_effect; } // Make sure the pixmaps are freed and any dangling effects // are cleaned up. if (t_effects != NULL) { /* OVERHAUL - REVISIT: error cleanup needs revised */ MCGImageRelease(t_initial_image); // MCGSurfaceRelease(t_final_image); while(t_effects != NULL) { MCEffectList *t_current_effect; t_current_effect = t_effects; t_effects = t_effects -> next; delete t_current_effect; } } MCRegionDestroy(t_effect_region); MCGImageRelease(m_snapshot); m_snapshot = nil; m_snapshot = t_initial_image; // Unlock the screen. MCRedrawUnlockScreen(); // Unlock messages. MClockmessages = t_old_lockmessages; // Turn off effect mode. state &= ~CS_EFFECT; // The stack's blendlevel is now the new one. old_blendlevel = blendlevel; // Finally, mark the affected area of the stack for a redraw. dirtyrect(p_area); }
bool MCUIDC::device_getwindowgeometry(Window p_window, MCRectangle &r_rect) { r_rect = MCU_make_rect(0, 0, 32, 32); return true; }
MCRectangle MCRegionGetBoundingBox(MCRegionRef self) { Rect t_rect; GetRegionBounds((RgnHandle)self, &t_rect); return MCU_make_rect(t_rect . left, t_rect . top, t_rect . right - t_rect . left, t_rect . bottom - t_rect . top); }