Draw an image from a pixel array.

1,068 views
Skip to first unread message

ar...@nextvr.com

unread,
Jun 16, 2017, 7:11:18 PM6/16/17
to skia-discuss
There is no code examples that I have yet seen that will draw a simple bitmap from a 1-d array of pixels and their respective colors. Can some one please give me a simple example. Thanks :) 

Shawn Riordan

unread,
Jun 16, 2017, 9:28:55 PM6/16/17
to skia-discuss
What do you mean by 1-d array of pixels?

Do you mean you have a pointer to some memory and you want Skia to treat that memory like it was a raster image?
How many bytes per pixel?

Do you mean 1d as in an image with only width (or only height)?

Hal Canary

unread,
Jun 17, 2017, 9:14:08 AM6/17/17
to skia-discuss
On Fri, Jun 16, 2017 at 7:11 PM, <ar...@nextvr.com> wrote:
There is no code examples that I have yet seen that will draw a simple bitmap from a 1-d array of pixels and their respective colors. Can some one please give me a simple example. Thanks :) 

Mike Reed

unread,
Jun 17, 2017, 8:40:06 PM6/17/17
to skia-d...@googlegroups.com
That is a good fiddle.

You can also use a variant called SkImage::MakeFromRaster(pixmap, proc, ctx) if you want to avoid making a copy of the pixels. You can pass null for the last 2 parameters if you know the pixels will outlive the image object.

--
You received this message because you are subscribed to the Google Groups "skia-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss+unsubscribe@googlegroups.com.
To post to this group, send email to skia-d...@googlegroups.com.
Visit this group at https://groups.google.com/group/skia-discuss.
For more options, visit https://groups.google.com/d/optout.

ar...@nextvr.com

unread,
Jun 19, 2017, 12:57:56 PM6/19/17
to skia-discuss
Perfect Hal, thanks !

Hal Canary

unread,
Jun 19, 2017, 1:15:06 PM6/19/17
to skia-discuss
On Sat, Jun 17, 2017 at 8:39 PM, 'Mike Reed' via skia-discuss <skia-d...@googlegroups.com> wrote:
You can also use a variant called SkImage::MakeFromRaster(pixmap, proc, ctx) if you want to avoid making a copy of the pixels. You can pass null for the last 2 parameters if you know the pixels will outlive the image object.

Which Canvases make the guarantee that they won't ref the SkImage passed in?  Raster and Gpu? SkPDF does not.

Mike Reed

unread,
Jun 19, 2017, 1:58:39 PM6/19/17
to skia-d...@googlegroups.com
If you know the lifetime of the canvas, or the pixel-memory is global, you're safe.

--

ar...@nextvr.com

unread,
Jun 21, 2017, 1:43:37 PM6/21/17
to skia-discuss
Now I am trying to output the image to a file on disk and it's always blank.


constexpr int SkiaWidth = 1920;
constexpr int SkiaHeight = 1080;

//OUTPUT GRAYSCALE VERION OF THE OUTPUT IMAGE
 size_t
ImageBufferGrayScaleSize = SkiaHeight * SkiaWidth;
 uint32_t
* ImageBufferGrayScale = new uint32_t[ImageBufferGrayScaleSize];

 
for (int i = 0; i < imageRows * imageCols; ++i)
 
{
 
ImageBufferGrayScale[i] = 0xFF0000A1;
 
}

FILE
* pFile;
 errno_t errFile
;

 
try {
 errFile
= fopen_s(&pFile, "myfile.png", "wb+");
 
assert(pFile);


 
if (errFile == 0)
 
{
 printf
("The file 'myfile.png' was opened\n");
 
}
 
else
 
{
 printf
("The file 'myfile.png' was not opened\n");
 
}
 
}
 
catch ( std::exception ex ){
 std
::cout << ex.what() << std::endl;
 
}

 
SkPixmap pixmap(SkImageInfo::Make(SkiaWidth, SkiaHeight, kN32_SkColorType, kPremul_SkAlphaType), ImageBufferGrayScale, sizeof(uint32_t) * ImageBufferGrayScaleSize);

 
if (!rasterCanvas->peekPixels(&pixmap))
 
{
 printf
("peekPixels false\n");
 
}
 
SkBitmap bm;
 bm
.installPixels(pixmap);
 rasterCanvas
->drawBitmap(bm, 0, 0, nullptr);
 
//fwrite(png->data(), png->size(), 1, pFile);

 sk_sp
<SkImage> img = rasterSurface->makeImageSnapshot();
 sk_sp
<SkData> png(img->encode(SkEncodedImageFormat::kPNG, 100));


 fwrite
(png->data(), png->size(), 1, pFile);


 fclose
(pFile);





On Saturday, June 17, 2017 at 6:14:08 AM UTC-7, Hal Canary wrote:

Hal Canary

unread,
Jun 21, 2017, 3:16:27 PM6/21/17
to skia-discuss
On Wed, Jun 21, 2017 at 1:43 PM, <ar...@nextvr.com> wrote:
Now I am trying to output the image to a file on disk and it's always blank.

Is it a blank image or an empty file? 

ar...@nextvr.com

unread,
Jun 21, 2017, 3:25:08 PM6/21/17
to skia-discuss
Always a black Image.

Brian Osman

unread,
Jun 21, 2017, 4:18:54 PM6/21/17
to skia-d...@googlegroups.com
The code you posted is a little confusing - it appears to be missing some context, and also doing some rather circular logic. imageRows vs. SkiaHeight? More importantly:

// This creates an SkPixmap object that points to the data stored in ImageBufferGrayscale.
SkPixmap pixmap(SkImageInfo::Make(SkiaWidth, SkiaHeight, kN32_SkColorType, kPremul_SkAlphaType), ImageBufferGrayScale, sizeof(uint32_t) * ImageBufferGrayScaleSize);


// This overwrites the pixmap object, so that it now points at the data from rasterCanvas.
// The data stored in ImageBufferGrayscale is unaffected -- you're now pointing directly
// at the raster surface's pixel data instead.
 
if (!rasterCanvas->peekPixels(&pixmap))

 
{
   printf
("peekPixels false\n");
 
}

// This creates another SkBitmap object, and makes it *also* point at the pixels
// that are being pointed to by pixmap. So this bitmap now also refers directly
// to the pixels in the surface that you used to create rasterCanvas.
 
SkBitmap bm;
 bm
.installPixels(pixmap);

// This now tries to draw bm, which contains the contents of rasterSurface, back
// to rasterCanvas - effectively just draws back to itself. Some other Skia devs
// would have to say for sure, but I'm not even positive that this is safe.
 rasterCanvas->drawBitmap(bm, 0, 0, nullptr);

 
//fwrite(png->data(), png->size(), 1, pFile);

 sk_sp
<SkImage> img = rasterSurface->makeImageSnapshot();
 sk_sp
<SkData> png(img->encode(SkEncodedImageFormat::kPNG, 100));

...

If you just want to fill out a pixmap and write it to disk as an image, you can do that with SkImage::MakeRasterCopy or SkImage::MakeFromRaster. If you do want to "draw" the data (perhaps with other drawing) and then get the image, you can use code like what you did above, but you should remove the peekPixels call - that's basically trashing your pixmap so that it no longer refers to your static pixel data.

NextVR, Inc. technology is protected by US Patents 8,451,320, 8,610,757, 9,204,127, 9,313,474, 9,538,160, 9,485,494 and 9,407,902 with additional patents pending. The information contained in this transmission contains privileged and confidential information. It is intended only for the use of the person(s) named above. If you are not the intended recipient, you are hereby notified that any review, dissemination, distribution or duplication of this communication is strictly prohibited. If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message.

ar...@nextvr.com

unread,
Jun 21, 2017, 7:21:43 PM6/21/17
to skia-discuss
Still crashing.


FILE
* pFile;
 errno_t errFile
;


 
try {
 errFile
= fopen_s(&pFile, "myfile.png", "wb+");
 
assert(pFile);


 
if (errFile == 0)
 
{
 printf
("The file 'myfile.png' was opened\n");
 
}
 
else
 
{
 printf
("The file 'myfile.png' was not opened\n");
 
}
 
}
 
catch ( std::exception ex ){
 std
::cout << ex.what() << std::endl;
 
}





 
SkPixmap pixmap(SkImageInfo::Make(SkiaWidth, SkiaHeight, kN32_SkColorType, kPremul_SkAlphaType), ImageBufferGrayScale, sizeof(uint32_t) * ImageBufferGrayScaleSize);


 sk_sp
<SkImage> img = SkImage::MakeRasterCopy(pixmap);



sk_sp
<SkData> png(img->encode(SkEncodedImageFormat::kPNG, 100));




fwrite
(png->data(), png->size(), 1, pFile);


 fclose
(pFile);
To unsubscribe from this group and stop receiving emails from it, send an email to skia-discuss...@googlegroups.com.

Hal Canary

unread,
Jun 22, 2017, 8:19:33 AM6/22/17
to skia-discuss
This seems to work for me:

#include <stdio.h>
#include "SkImage.h"
static void f() {
    constexpr int SkiaWidth = 1920;
    constexpr int SkiaHeight = 1080;
    constexpr size_t ImageBufferGrayScaleSize = SkiaHeight * SkiaWidth;
    uint32_t* ImageBufferGrayScale = new uint32_t[ImageBufferGrayScaleSize];
    for (size_t i = 0; i < ImageBufferGrayScaleSize; ++i) { 
        ImageBufferGrayScale[i] = 0xFF0000A1;
    }
    size_t rowBytes = sizeof(uint32_t) * SkiaWidth;
    SkPixmap pixmap(SkImageInfo::Make(SkiaWidth, SkiaHeight,
                                      kN32_SkColorType, kPremul_SkAlphaType),
                    ImageBufferGrayScale, rowBytes);
    sk_sp<SkImage> img = SkImage::MakeRasterCopy(pixmap);
    assert(img);
    delete ImageBufferGrayScale;
    sk_sp<SkData> png(img->encode(SkEncodedImageFormat::kPNG, 100));
    assert(png);

    FILE* pFile = fopen("myfile.png", "wb+");
    if (!pFile) {
        printf("The file 'myfile.png' was not opened\n");
        return;

Hal Canary

unread,
Jun 22, 2017, 9:48:37 AM6/22/17
to skia-discuss
It was politely suggested to me that my example code should be a *better* example, so here's version 2:

    #include <stdio.h>
    #include "SkImage.h"
    static void encode_to_file(SkImage* img, const char* path) {
        assert(img);
        sk_sp<SkData> png(img->encode(SkEncodedImageFormat::kPNG, 100));
        assert(png);
        FILE* pFile = fopen(path, "wb+");
        if (!pFile) {
            fprintf(stderr, "The file '%s' was not opened\n", path);
            return;
        }
        fwrite(png->data(), png->size(), 1, pFile);
        fclose(pFile);
    }
    sk_sp<SkImage> make_skia_image(int width, int height, std::unique_ptr<uint32_t[]> pixels) {
        SkPixmap pixmap(SkImageInfo::Make(width, height,
                                          kN32_SkColorType, kPremul_SkAlphaType),
                        pixels.release(), sizeof(uint32_t) * width);
        // SkImage now owns the array;
        return SkImage::MakeFromRaster(
                pixmap, [](const void* p, void*) { delete[] (uint32_t*)p; }, nullptr);
    }
    // make a 2d gradient
    sk_sp<SkImage> make_example_image() {
        int width = 1920;
        int height = 1080;
        std::unique_ptr<uint32_t[]> pixels(new uint32_t[height * width]);
        for (int y = 0; y < height; ++y) { 
            for (int x = 0; x < width; ++x) { 
                unsigned g = x * 255 / (width - 1);
                unsigned b = y * 255 / (height - 1);
                pixels[y * width + x] =
                        SkPreMultiplyARGB(0xFF, 0x00, g, b);
            }
        }
        return make_skia_image(width, height, std::move(pixels));
    }
    static void f() {
        sk_sp<SkImage> img = make_example_image();
        encode_to_file(img.get(), "myfile.png");    
    }

This is tested and works for me.

ar...@nextvr.com

unread,
Jun 22, 2017, 3:00:31 PM6/22/17
to skia-discuss
Ahh the mistake I made was that I thought to instantiate the pixmap, it needs the total number of bytes whereas it just require the bytes / row.
Reply all
Reply to author
Forward
0 new messages