Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WallPaper Image #140

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft

WallPaper Image #140

wants to merge 1 commit into from

Conversation

y4my4my4m
Copy link
Collaborator

The basic implemlentation is done.
A few things to fix:

  • Start the DC start after the main menu so as to not overwrite it
  • Dynamically recognize the resolution and assign it to the proper folder
  • Hotkey change wallpaper
  • The uPNG lib is being reused in multiple apps now, would be nice to decide where to put it and make it globally available.

Screenshot_20231118_203115

@y4my4my4m y4my4my4m self-assigned this Nov 19, 2023
@y4my4my4m y4my4my4m linked an issue Nov 19, 2023 that may be closed by this pull request
@GutPuncher
Copy link
Contributor

The uPNG lib is being reused in multiple apps now, would be nice to decide where to put it and make it globally available.

The LICENSE of the uPNG lib is not compatible with Zeal's public domain license. A public domain / Creative Commons Zero / Zero-Clause BSD licensed PNG library implementation must be used for the library to be eligible to be merged to the OS repo. This could be done by finding one that has a compatible license, or to delete the existing library file(s) and begin reverse engineering a compatible-license PNG library from scratch. A simpler choice may be to instead make the software requiring a non-public-domain library to be a 'third-party' application, like ported emulators, living in its own separate repository at the Organization-level that can be optionally git cloned into Zeal's src/Home/ folder.

@GutPuncher GutPuncher marked this pull request as draft December 10, 2023 17:35
@y4my4my4m
Copy link
Collaborator Author

Good catch, I'll see what I can do.

@SolsticeSpectrum
Copy link

Good catch, I'll see what I can do.

Why not use bitmap? Converting png to bitmap is effortless and bitmaps are also much easier to implement.

@xslendix
Copy link
Collaborator

xslendix commented Nov 28, 2024

Why not use bitmap?

It would be nice to have support for more used image formats.

@SolsticeSpectrum
Copy link

Why not use bitmap?

It would be nice to have support for more used image formats.

Not possible if they wanna comply with licenses

@xslendix
Copy link
Collaborator

Not possible if they wanna comply with licenses

It is very very possible, take stb libraries for example. There's stb_image with support for a lot of formats.

@y4my4my4m
Copy link
Collaborator Author

y4my4my4m commented Nov 28, 2024

stb_image does look like a good alternative.

Otherwise maybe something like

// Simplified PNG Reader in HolyC
CDC *PNGRead(U8 *filename) {
    U8 *file_data;
    I64 file_size;
    U8 *ptr;
    U8 *end;
    U8 signature[8];
    U64 chunk_length;
    U8 chunk_type[4];
    U8 *chunk_data;
    U32 crc;
    U64 width, height;
    U8 bit_depth, color_type, compression_method, filter_method, interlace_method;
    U8 *idat_data = NULL;
    U64 idat_size = 0;

    // Read the file
    file_data = FileRead(filename, &file_size);
    if (file_data == NULL) {
        Print("Failed to read file: %s\n", filename);
        return NULL;
    }
    ptr = file_data;
    end = file_data + file_size;

    // Step 1: Verify PNG signature
    MemCopy(signature, ptr, 8);
    ptr += 8;
    if (MemCmp(signature, "\x89PNG\x0D\x0A\x1A\x0A", 8) != 0) {
        Print("Invalid PNG signature\n");
        Free(file_data);
        return NULL;
    }

    // Step 2: Read chunks
    while (ptr < end) {
        // Read chunk length (big-endian)
        if (ptr + 8 > end) break;
        chunk_length = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
        ptr += 4;

        // Read chunk type
        MemCopy(chunk_type, ptr, 4);
        ptr += 4;

        // Ensure we don't read beyond the file
        if (ptr + chunk_length + 4 > end) {
            Print("Unexpected end of file\n");
            break;
        }

        chunk_data = ptr;
        ptr += chunk_length;

        // Read CRC (we'll skip CRC checking for simplicity)
        crc = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
        ptr += 4;

        // Process chunk based on type
        if (MemCmp(chunk_type, "IHDR", 4) == 0) {
            // Process IHDR chunk
            width = (chunk_data[0] << 24) | (chunk_data[1] << 16) | (chunk_data[2] << 8) | chunk_data[3];
            height = (chunk_data[4] << 24) | (chunk_data[5] << 16) | (chunk_data[6] << 8) | chunk_data[7];
            bit_depth = chunk_data[8];
            color_type = chunk_data[9];
            compression_method = chunk_data[10];
            filter_method = chunk_data[11];
            interlace_method = chunk_data[12];

            // Only support 8-bit RGB images without interlacing
            if (bit_depth != 8 || color_type != 2 || compression_method != 0 || filter_method != 0 || interlace_method != 0) {
                Print("Unsupported PNG format\n");
                Free(file_data);
                return NULL;
            }
        } else if (MemCmp(chunk_type, "IDAT", 4) == 0) {
            // Collect IDAT data
            if (idat_data == NULL) {
                idat_data = MAlloc(chunk_length);
                idat_size = chunk_length;
                MemCopy(idat_data, chunk_data, chunk_length);
            } else {
                // Concatenate data
                U8 *new_data = MAlloc(idat_size + chunk_length);
                MemCopy(new_data, idat_data, idat_size);
                MemCopy(new_data + idat_size, chunk_data, chunk_length);
                Free(idat_data);
                idat_data = new_data;
                idat_size += chunk_length;
            }
        } else if (MemCmp(chunk_type, "IEND", 4) == 0) {
            // End of PNG
            break;
        } else {
            // Skip other chunks
            ptr += chunk_length + 4; // Skip chunk data and CRC
        }
    }

    Free(file_data);

    if (idat_data == NULL) {
        Print("No image data found\n");
        return NULL;
    }

    // Step 3: Decompress IDAT data using DEFLATE
    // Since we cannot use licensed code, we'll implement a minimal DEFLATE decompressor
    // For the sake of this example, let's assume a function DecompressDEFLATE exists
    U8 *decompressed_data;
    U64 decompressed_size;
    decompressed_data = DecompressDEFLATE(idat_data, idat_size, &decompressed_size);
    Free(idat_data);

    if (decompressed_data == NULL) {
        Print("Failed to decompress image data\n");
        return NULL;
    }

    // Step 4: Process the decompressed data
    // Remove filter bytes and reconstruct image data
    U64 row_bytes = width * 3; // 3 bytes per pixel (RGB)
    U8 *image_data = MAlloc(height * row_bytes);
    U8 *src = decompressed_data;
    U8 *dst = image_data;

    for (U64 y = 0; y < height; y++) {
        if (src >= decompressed_data + decompressed_size) {
            Print("Unexpected end of decompressed data\n");
            Free(decompressed_data);
            Free(image_data);
            return NULL;
        }
        U8 filter_type = *src++;
        // For simplicity, only handle filter type 0 (None)
        if (filter_type != 0) {
            Print("Unsupported filter type: %d\n", filter_type);
            Free(decompressed_data);
            Free(image_data);
            return NULL;
        }
        // Copy the scanline
        MemCopy(dst, src, row_bytes);
        src += row_bytes;
        dst += row_bytes;
    }

    Free(decompressed_data);

    // Step 5: Create CDC and plot pixels
    CDC *dc = DCNew(width, height);
    if (dc == NULL) {
        Free(image_data);
        return NULL;
    }

    dst = image_data;
    for (U64 y = 0; y < height; y++) {
        for (U64 x = 0; x < width; x++) {
            U8 r = *dst++;
            U8 g = *dst++;
            U8 b = *dst++;
            dc->color = BMP24Color(r, g, b, 0); // Assuming BMP24Color accepts RGB values
            GrPlot(dc, x, y);
        }
    }

    Free(image_data);
    return dc;
}
U8 *DecompressDEFLATE(U8 *data, U64 data_size, U64 *out_size) {
    U8 *ptr = data;
    U8 *end = data + data_size;
    U8 *output = MAlloc(/* estimated output size */);
    U64 output_pos = 0;

    while (ptr < end) {
        U8 bfinal = *ptr & 1;
        U8 btype = (*ptr >> 1) & 0x03;
        ptr++;

        if (btype == 0) {
            // Uncompressed block
            if (ptr + 4 > end) break;
            U16 len = ptr[0] | (ptr[1] << 8);
            U16 nlen = ptr[2] | (ptr[3] << 8);
            ptr += 4;
            if (len != (~nlen & 0xFFFF)) {
                Print("Invalid uncompressed block length\n");
                Free(output);
                return NULL;
            }
            if (ptr + len > end) {
                Print("Unexpected end of data in uncompressed block\n");
                Free(output);
                return NULL;
            }
            MemCopy(output + output_pos, ptr, len);
            output_pos += len;
            ptr += len;
        } else {
            Print("Unsupported compression type\n");
            Free(output);
            return NULL;
        }

        if (bfinal) break;
    }

    *out_size = output_pos;
    return output;
}
CDC *BMPRead(U8 *filename) {
    U8 *file_data;
    I64 file_size;
    U8 *ptr;
    U64 width, height;
    U16 bits_per_pixel;
    U32 offset;
    U8 *pixel_data;

    // Read the file
    file_data = FileRead(filename, &file_size);
    if (file_data == NULL) {
        Print("Failed to read file: %s\n", filename);
        return NULL;
    }
    ptr = file_data;

    // Check BMP signature ('BM')
    if (ptr[0] != 'B' || ptr[1] != 'M') {
        Print("Invalid BMP file\n");
        Free(file_data);
        return NULL;
    }

    // Read offset to pixel data
    offset = ptr[10] | (ptr[11] << 8) | (ptr[12] << 16) | (ptr[13] << 24);

    // Read DIB header (assuming BITMAPINFOHEADER)
    width = ptr[18] | (ptr[19] << 8) | (ptr[20] << 16) | (ptr[21] << 24);
    height = ptr[22] | (ptr[23] << 8) | (ptr[24] << 16) | (ptr[25] << 24);
    bits_per_pixel = ptr[28] | (ptr[29] << 8);

    if (bits_per_pixel != 24) {
        Print("Unsupported BMP format (only 24-bit supported)\n");
        Free(file_data);
        return NULL;
    }

    pixel_data = file_data + offset;

    // Create CDC and plot pixels
    CDC *dc = DCNew(width, height);
    if (dc == NULL) {
        Free(file_data);
        return NULL;
    }

    // BMP stores pixels bottom-up
    for (U64 y = 0; y < height; y++) {
        U8 *row = pixel_data + (height - 1 - y) * ((width * 3 + 3) & ~3);
        for (U64 x = 0; x < width; x++) {
            U8 b = *row++;
            U8 g = *row++;
            U8 r = *row++;
            dc->color = BMP24Color(r, g, b, 0);
            GrPlot(dc, x, y);
        }
    }

    Free(file_data);
    return dc;
}

^ ai generated, could be senseless lol

@SolsticeSpectrum
Copy link

Please don't use AI to generate code for obscure operating system. There are several flaws.

Most important one is use of MAlloc without consideration to TempleOS's unique memory management. Temple uses heap allocator which isn't designed for large allocations. There is no garbage collection so memory fragmentation must be considered carefully. Large images could quickly exhaust heap.

DecompressDEFLATE implementation is extremely naive and only handles uncompressed blocks.

The code doesn't account for TempleOS color model. Temple uses specific 16 color palette so direct RGB conversion won't work properly.

I suggest using memory-mapped files or fixed-size buffers.
I still recommend uncompressed formats like BMP with fixed palette.

@y4my4my4m
Copy link
Collaborator Author

@SolsticeSpectrum it can work 32bit color can work.
image

https://github.com/y4my4my4m/Zeal32BitPNGViewer

The point of the code that I've shared was to give an idea of how it could be implemented manually without libraries. It wasn't meant to be used, more of a starting point if someone wants to pick it up.

@GutPuncher
Copy link
Contributor

Please don't use AI to generate code for obscure operating system. There are several flaws.

Most important one is use of MAlloc without consideration to TempleOS's unique memory management. Temple uses heap allocator which isn't designed for large allocations. There is no garbage collection so memory fragmentation must be considered carefully. Large images could quickly exhaust heap.

DecompressDEFLATE implementation is extremely naive and only handles uncompressed blocks.

The code doesn't account for TempleOS color model. Temple uses specific 16 color palette so direct RGB conversion won't work properly.

I suggest using memory-mapped files or fixed-size buffers. I still recommend uncompressed formats like BMP with fixed palette.

You can MAlloc entire gigabyte(s) allocations

Garbage collection in TOS forks is just "don't forget to Free when you're done"

Zeal is not TOS: i.e.: you can support 32 bit colors and the palette limitation is going to be deprecated some point in the next 100 years to use a 32bit color system that will deprecate the concept of palettes so don't get caught up on that

Compression is banned in Zeal's Charter so no image formats that use compression should be bothered with

@GutPuncher
Copy link
Contributor

From the Charter:

"No encryption, No compression. All formats, files, protocols, and algorithms must operate entirely in unobfuscated plain-text. Decrypting, encrypting, decompressing, and compressing creates redundant overhead that makes programs slow and complicated. Encoding/decoding of unencrypted uncompressed data in binary formats (.ZXE, .BIN, .GR, .GR32, DolDoc sprite binary data, etc.) is permitted."

@GutPuncher
Copy link
Contributor

I don't pretend to know whether PNG uses compression or not, but if it does, then it wouldn't be mainlined anyway

@SolsticeSpectrum
Copy link

From the Charter:

"No encryption, No compression. All formats, files, protocols, and algorithms must operate entirely in unobfuscated plain-text. Decrypting, encrypting, decompressing, and compressing creates redundant overhead that makes programs slow and complicated. Encoding/decoding of unencrypted uncompressed data in binary formats (.ZXE, .BIN, .GR, .GR32, DolDoc sprite binary data, etc.) is permitted."

So basically BMP is the best option

@GutPuncher
Copy link
Contributor

So basically BMP is the best option

Or, some new version of GR32, which would reduce the need for legacy cruft inherited from Microsoft

@GutPuncher
Copy link
Contributor

Or, some new version of GR32, which would reduce the need for legacy cruft inherited from Microsoft

https://github.com/Zeal-Operating-System/ZealOS/blob/master/src/Demo/Graphics/32BitPaint.ZC#L86

@SolsticeSpectrum
Copy link

SolsticeSpectrum commented Nov 29, 2024

Problem with new image format is that it won't be easily convertable. For bmp I can use any online png to bmp converter.

@GutPuncher
Copy link
Contributor

Problem with new image format is that it won't be easily convertable. For bmp I can use any online png to bmp converter.

Given that GR32 (in its current form) is literally just raw framebuffer 32-bit RGB (and in some possible future version, may solely just have metadata for X and Y dimensions), it would be very easy to convert to anything else

@GutPuncher
Copy link
Contributor

And similarly, any decent image library would have zero problem exporting out to a raw 32bit RGB bitmap representation of whatever image you may choose to throw at it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Allow setting an image file as your wallpaper.
4 participants