Example #1
0
    const BitmapData* BitmapData::fromAsset(const char* name)
    {
        loom_asset_image_t* img = static_cast<loom_asset_image_t*>(loom_asset_lock(name, LATImage, 1));
        loom_asset_unlock(name);

        if (img == NULL)
            return NULL;

        BitmapData* result = lmNew(NULL) BitmapData(
            (size_t)img->width,
            (size_t)img->height
        );

        if (result == NULL) {
            lmLogError(gGFXLogGroup, "Unable to allocate memory for BitmapData asset data");
            return NULL;
        }

        memcpy(result->data, img->bits, img->width * img->height * DATA_BPP);

        return result;
    }
Example #2
0
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;
}