static void pollScaling() { // Check the event queue. if (gEventQueueMutex == NULL) { return; } loom_mutex_lock(gEventQueueMutex); while (gEventQueue.size() > 0) { RescaleEventStatus curItem = gEventQueue.front(); gImageScaleProgressDelegate.pushArgument(curItem.path.c_str()); gImageScaleProgressDelegate.pushArgument(curItem.progress); gImageScaleProgressDelegate.invoke(); // Flush the asset (only works on main thread atm) if (curItem.progress == 1.0f) { loom_asset_flush(curItem.path.c_str()); } gEventQueue.pop_front(); } loom_mutex_unlock(gEventQueueMutex); }
// removes all breakpoints static void removeAllBreakpoints() { for (UTsize i = 0; i < breakpoints.size(); i++) { delete breakpoints.at(i); } breakpoints.clear(); regenerateSourceBreakpoints(); }
// removes the breakpoint at the given index static void removeBreakpointAtIndex(int index) { if ((index < 0) || (index >= (int)breakpoints.size())) { return; } Breakpoint *bp = breakpoints.at(index); breakpoints.erase(bp); delete bp; regenerateSourceBreakpoints(); }
// removes a breakpoint at the given source and line static void removeBreakpoint(const char *source, int line) { for (UTsize i = 0; i < breakpoints.size(); i++) { Breakpoint *bp = breakpoints.at(i); if ((bp->source == source) && (bp->line == line)) { breakpoints.erase(bp); delete bp; } } regenerateSourceBreakpoints(); }
// retrieves a Vector of system.Breakpoints corresponding // to the current breakpoints static int getBreakpoints(lua_State *L) { Type *vectorType = LSLuaState::getLuaState(L)->getType("system.Vector"); Type *bpType = LSLuaState::getLuaState(L)->getType("system.Breakpoint"); int sourceOrdinal = bpType->getMemberOrdinal("source"); int lineOrdinal = bpType->getMemberOrdinal("line"); // create the vector instance lsr_createinstance(L, vectorType); // store the length lsr_vector_set_length(L, -1, breakpoints.size()); lua_rawgeti(L, -1, LSINDEXVECTOR); int vidx = lua_gettop(L); // loop through the current breakpoints and setup // data for (UTsize i = 0; i < breakpoints.size(); i++) { Breakpoint *bp = breakpoints.at(i); lsr_createinstance(L, bpType); int bpIdx = lua_gettop(L); lua_pushnumber(L, sourceOrdinal); lua_pushstring(L, bp->source.c_str()); lua_rawset(L, bpIdx); lua_pushnumber(L, lineOrdinal); lua_pushnumber(L, bp->line); lua_rawset(L, bpIdx); lua_rawseti(L, vidx, i); } lua_pop(L, 1); // pop vector table // return Vector return 1; }
static void regenerateSourceBreakpoints() { sourceBreakpoints.clear(); for (UTsize i = 0; i < breakpoints.size(); i++) { Breakpoint *bp = breakpoints.at(i); utFastStringHash fhash(bp->source); if (sourceBreakpoints.find(fhash) == UT_NPOS) { utArray<Breakpoint *> bps; sourceBreakpoints.insert(fhash, bps); } sourceBreakpoints.get(fhash)->push_back(bp); } }
static CallbackQueueNote *dequeueCallback() { CallbackQueueNote *cqn = NULL; loom_mutex_lock(gCallbackLock); if (gCallbackQueue.begin() == NULL) { cqn = NULL; } else { cqn = gCallbackQueue.front(); gCallbackQueue.pop_front(); } loom_mutex_unlock(gCallbackLock); return cqn; }
static void enqueueFileChangeCallback(const char *path) { CallbackQueueNote *cqn = lmNew(NULL) CallbackQueueNote(); cqn->type = QNT_Change; cqn->text = utString(path); loom_mutex_lock(gCallbackLock); gCallbackQueue.push_back(cqn); loom_mutex_unlock(gCallbackLock); }
static void enqueueLogCallback(const char *msg) { CallbackQueueNote *cqn = lmNew(NULL) CallbackQueueNote(); cqn->type = QNT_Log; cqn->text = utString(msg); loom_mutex_lock(gCallbackLock); gCallbackQueue.push_back(cqn); loom_mutex_unlock(gCallbackLock); }
// add's a breakpoint at the given source and line, checks for duplicates // and avoids them static void addBreakpoint(const char *source, int line) { Breakpoint *bp; for (UTsize i = 0; i < breakpoints.size(); i++) { bp = breakpoints.at(i); if ((bp->source == source) && (bp->line == line)) { return; } } bp = new Breakpoint; bp->source = source; bp->line = line; breakpoints.push_back(bp); regenerateSourceBreakpoints(); }
static void postResampleEvent(const char *path, float progress) { if (gEventQueueMutex == NULL) { gEventQueueMutex = loom_mutex_create(); } loom_mutex_lock(gEventQueueMutex); RescaleEventStatus res; res.path = path; res.progress = progress; gEventQueue.push_back(res); loom_mutex_unlock(gEventQueueMutex); }
void Assembly::getLoadedAssemblies(LSLuaState *vm, utList<Assembly *>& oassemblies) { utHashTable<utHashedString, Assembly *> *lookup = NULL; UTsize idx = assemblies.find(vm); if (idx != UT_NPOS) { lookup = assemblies.at(idx); } else { return; } for (UTsize i = 0; i < lookup->size(); i++) { oassemblies.push_back(lookup->at(i)); } }
// Main lua VM debug hook static void debugHook(lua_State *L, lua_Debug *ar) { int top = lua_gettop(L); // line event if ((ar->event == LUA_HOOKLINE) && lineEventDelegate.getCount() && !assertion) { // If we're finishing an method we are not interested // in line events until we have returned from the method if (finishMethod) { return; } // if we're not stepping, have no breakpoints, and haven't hit a Debug.debug() // we are not interested in the line event if (!stepping && !breakpoints.size() && !debugBreak) { return; } // get the call stack at this line getCallStack(L, LINE_EVENT); // if we don't have a valid stack, return if (lua_isnil(L, -1)) { lua_pop(L, 1); return; } // call the native delegate lineEventDelegate.incArgCount(); lineEventDelegate.invoke(); } // return hook if ((ar->event == LUA_HOOKRET) && returnEventDelegate.getCount() && !assertion) { // if we don't have a method we're finishing, we // don't care if (!finishMethod) { return; } // get the call stack getCallStack(L, RETURN_EVENT); // invalid stack? No problem, bail if (lua_isnil(L, -1)) { lua_pop(L, 1); return; } // call the debugger's native delegate returnEventDelegate.incArgCount(); returnEventDelegate.invoke(); } // call hook if ((ar->event == LUA_HOOKCALL) && callEventDelegate.getCount() && !assertion) { // we on;y care about call events when stepping over if (!blocking || !stepping || !stepOver) { return; } // get the call stack for this event getCallStack(L, CALL_EVENT); // invalid? If so, bail if (lua_isnil(L, -1)) { lua_pop(L, 1); return; } // call the debugger's event delegate callEventDelegate.incArgCount(); callEventDelegate.invoke(); } lua_settop(L, top); }
namespace GFX { /** * This is the implementation of the background-threaded image rescaling API. * Someday it should live elsewhere than in this file. For the meanwhile, it * is a cozy home! */ struct RescaleEventStatus { utString path; float progress; }; struct RescaleNote { utString outPath; utString inPath; int outWidth; int outHeight; bool preserveAspect; }; static MutexHandle gEventQueueMutex = NULL; static utList<RescaleEventStatus> gEventQueue; static LS::NativeDelegate gImageScaleProgressDelegate; static void pollScaling() { // Check the event queue. if (gEventQueueMutex == NULL) { return; } loom_mutex_lock(gEventQueueMutex); while (gEventQueue.size() > 0) { RescaleEventStatus curItem = gEventQueue.front(); gImageScaleProgressDelegate.pushArgument(curItem.path.c_str()); gImageScaleProgressDelegate.pushArgument(curItem.progress); gImageScaleProgressDelegate.invoke(); // Flush the asset (only works on main thread atm) if (curItem.progress == 1.0f) { loom_asset_flush(curItem.path.c_str()); } gEventQueue.pop_front(); } loom_mutex_unlock(gEventQueueMutex); } static void postResampleEvent(const char *path, float progress) { if (gEventQueueMutex == NULL) { gEventQueueMutex = loom_mutex_create(); } loom_mutex_lock(gEventQueueMutex); RescaleEventStatus res; res.path = path; res.progress = progress; gEventQueue.push_back(res); loom_mutex_unlock(gEventQueueMutex); } static int __stdcall scaleImageOnDisk_body(void *param) { // Grab our arguments. RescaleNote *rn = (RescaleNote *)param; const char *outPath = rn->outPath.c_str(); const char *inPath = rn->inPath.c_str(); int outWidth = rn->outWidth; int outHeight = rn->outHeight; bool preserveAspect = rn->preserveAspect; // Load the image. We always work in 4 components (rgba). int t0 = platform_getMilliseconds(); loom_asset_image *lai = NULL; // Load async since we're in a background thread. while ((lai = (loom_asset_image *)loom_asset_lock(inPath, LATImage, 0)) == NULL) { loom_thread_yield(); } int imageX = lai->width; int imageY = lai->height; stbi_uc *imageBits = (stbi_uc *)lai->bits; lmLog(gGFXTextureLogGroup, "Image setup took %dms", t0 - platform_getMilliseconds()); int t1 = platform_getMilliseconds(); // Resize to fit within the specified size preserving aspect ratio, if flag is set. if (preserveAspect) { float scaleX = float(outWidth) / float(imageX); float scaleY = float(outHeight) / float(imageY); float actualScale = (scaleX < scaleY) ? scaleX : scaleY; outWidth = (int)(imageX * actualScale); outHeight = (int)(imageY * actualScale); lmLog(gGFXTextureLogGroup, "Scale to %d %d due to scale %f %f actual=%f", outWidth, outHeight, scaleX, scaleY, actualScale); } // Build a buffer for byte->float conversions... Resampler::Sample *buffRed = (Resampler::Sample *)malloc(sizeof(Resampler::Sample) * imageX); Resampler::Sample *buffGreen = (Resampler::Sample *)malloc(sizeof(Resampler::Sample) * imageX); Resampler::Sample *buffBlue = (Resampler::Sample *)malloc(sizeof(Resampler::Sample) * imageX); // And the downsampled image. Give a slight margin because the scaling routine above can give // up to outWidth inclusive as an output value. stbi_uc *outBuffer = (stbi_uc *)malloc(sizeof(stbi_uc) * 3 * (outWidth + 1) * (outHeight + 1)); // Set up the resamplers, reusing filter constants. const char *pFilter = "blackman"; float filter_scale = 1.0; Resampler resizeR(imageX, imageY, outWidth, outHeight, Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f, pFilter, NULL, NULL, filter_scale, filter_scale); Resampler resizeG(imageX, imageY, outWidth, outHeight, Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f, pFilter, resizeR.get_clist_x(), resizeR.get_clist_y(), filter_scale, filter_scale); Resampler resizeB(imageX, imageY, outWidth, outHeight, Resampler::BOUNDARY_CLAMP, 0.0f, 1.0f, pFilter, resizeR.get_clist_x(), resizeR.get_clist_y(), filter_scale, filter_scale); int resultY = 0; lmLog(gGFXTextureLogGroup, "Resample setup took %dms", t1 - platform_getMilliseconds()); int t2 = platform_getMilliseconds(); // Process each row of the image. for (int y = 0; y < imageY; y++) { // Deinterleave each row. for (int x = 0; x < imageX; x++) { buffRed[x] = Resampler::Sample(imageBits[(y * imageX * 4) + (x * 4) + 0]) / Resampler::Sample(255.f); buffGreen[x] = Resampler::Sample(imageBits[(y * imageX * 4) + (x * 4) + 1]) / Resampler::Sample(255.f); buffBlue[x] = Resampler::Sample(imageBits[(y * imageX * 4) + (x * 4) + 2]) / Resampler::Sample(255.f); } // Submit to resampler. lmAssert(resizeR.put_line(buffRed), "bad red"); lmAssert(resizeG.put_line(buffGreen), "bad green"); lmAssert(resizeB.put_line(buffBlue), "bad blue"); // If there are results, reinterleave and consume them. while (resizeR.check_line() && resizeG.check_line() && resizeB.check_line() && resultY < outHeight) { const Resampler::Sample *outRowR = resizeR.get_line(); const Resampler::Sample *outRowG = resizeG.get_line(); const Resampler::Sample *outRowB = resizeB.get_line(); if (outRowR || outRowG || outRowB) { lmAssert(outRowR && outRowG && outRowB, "Somehow got one line without others!"); } else { break; } // Find the row for output. stbi_uc *imageOutBits = outBuffer + (resultY * outWidth * 3); resultY++; for (int i = 0; i < outWidth; i++) { imageOutBits[i * 3 + 0] = int(outRowR[i] * Resampler::Sample(255.f)); imageOutBits[i * 3 + 1] = int(outRowG[i] * Resampler::Sample(255.f)); imageOutBits[i * 3 + 2] = int(outRowB[i] * Resampler::Sample(255.f)); } // Every hundred lines post an update. if (resultY % 100 == 0) { postResampleEvent(outPath, (float)resultY / (float)outHeight); } } } lmLog(gGFXTextureLogGroup, "Resample took %dms", t2 - platform_getMilliseconds()); // Write it back out. int t3 = platform_getMilliseconds(); jpge::compress_image_to_jpeg_file(outPath, outWidth, outHeight, 3, outBuffer); lmLog(gGFXTextureLogGroup, "JPEG output took %dms", t3 - platform_getMilliseconds()); // Post completion event. postResampleEvent(outPath, 1.0); // Free everything! loom_asset_unlock(inPath); free(buffRed); free(buffGreen); free(buffBlue); free(outBuffer); delete rn; return 0; } static void scaleImageOnDisk(const char *outPath, const char *inPath, int outWidth, int outHeight, bool preserveAspect) { RescaleNote *rn = new RescaleNote(); rn->outPath = outPath; rn->inPath = inPath; rn->outWidth = outWidth; rn->outHeight = outHeight; loom_thread_start(scaleImageOnDisk_body, rn); } static const NativeDelegate *getImageScaleProgressDelegate() { return &gImageScaleProgressDelegate; } static int registerLoomGraphics(lua_State *L) { beginPackage(L, "loom.graphics") .beginClass<Texture> ("Texture2D") .addStaticMethod("initFromAsset", &Texture::initFromAssetManager) .addStaticMethod("dispose", &Texture::dispose) .addStaticMethod("scaleImageOnDisk", &scaleImageOnDisk) .addStaticMethod("pollScaling", &pollScaling) .addStaticProperty("imageScaleProgress", &getImageScaleProgressDelegate) .endClass() .beginClass<Graphics> ("Graphics") .addStaticMethod("handleContextLoss", &Graphics::handleContextLoss) .addStaticMethod("screenshot", &Graphics::screenshot) .addStaticMethod("setDebug", &Graphics::setDebug) .addStaticMethod("setFillColor", &Graphics::setFillColor) .endClass() .beginClass<TextureInfo> ("TextureInfo") .addVar("width", &TextureInfo::width) .addVar("height", &TextureInfo::height) .addVar("id", &TextureInfo::id) .addVarAccessor("update", &TextureInfo::getUpdateDelegate) .endClass() .endPackage(); return 0; } }