Creating JPEG images with specified dimensions. How can you add W x H sizing to an image?

I have been searching for a Deno/TypeScript code snippet that can create basic images with dimensions embedded on them. I have provided an example of the code below, which generates images in JPEG format, base64, and dataURL.

The code works by adding RGB pixels one by one into a Number Array to build the image.

// Import jpeg encoder
import { encode as pixelsToBin } from "https://deno.land/x/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2d474d584e5d5a7948504f">[email protected]</a>/mod.ts";
// Import std base64 encoder
import { encode as binTob64 } from "https://deno.land/std/encoding/base64.ts";

// Create a jpeg Image with RED, GREEN, BLUE & ALPHA colors

/***
 * 
 * Material Design Colors | Scale 500
 * 
 * 19 colors
 * 
 * RED ---------> #f44336 -> rgba(244, 67, 54, 1)
 * PINK --------> #e91e63 -> rgba(233, 30, 99, 1)
 * PURPLE ------> #9c27b0 -> rgba(156, 39, 176, 1)
 * DEEP-PURPLE --> #673ab7 -> rgba(103, 58, 183, 1)
 * INDIGO ------> #3f51b5 -> rgba(63, 81, 181, 1)
 * BLUE --------> #2196f3 -> rgba(63, 81, 181, 1)
 * LIGHT-BLUE --> #03a9f4 -> rgba(3, 169, 244, 1)
 * CYAN --------> #00bcd4 -> rgba(0, 188, 212, 1)
 * TEAL --------> #009688 -> rgba(0, 150, 136, 1)
 * GREEN -------> #4caf50 -> rgba(76, 175, 80, 1)
 * LIGHT-GREEN -> #8bc34a -> rgba(139, 195, 74, 1)
 * LIME --------> #cddc39 -> rgba(205, 220, 57, 1)
 * YELLOW ------> #ffeb3b -> rgba(255, 235, 59, 1)
 * AMBER -------> #ffc107 -> rgba(255, 193, 7, 1)
 * ORANGE ------> #ff9800 -> rgba(255, 152, 0, 1)
 * DEEP-ORANGE -> #ff5722 -> rgba(255, 87, 34, 1)
 * BROWN -------> #795548 -> rgba(121, 85, 72, 1)
 * GREY --------> #9e9e9e -> rgba(158, 158, 158, 1)
 * BLUE-GREY ---> #607d8b -> rgba(96, 125, 139, 1)
 * 
 */

// Function to convert HEX color to RGB color
function hexToRgb(cHex): Array<number> {
  const r = parseInt(cHex.slice(1, 3), 16);
  const g = parseInt(cHex.slice(3, 5), 16);
  const b = parseInt(cHex.slice(5, 7), 16);
  // return {r, g, b} // return an object
  return [ r, g, b ];
}

async function saveJpeg(w: number, h: number, cHex: string, fileName: string) {

  // Dimensions of the generated jpeg image
  const jpegWidth:  number = w;
  const jpegHeight: number = h;
  const jpegArea:   number = (jpegWidth * jpegHeight);

  // Set RGB color based on input HEX color
  const jpegPixelColor: number[] = hexToRgb(cHex);

  // Initialize the array to store pixel values
  let jpegPixels: number[] = [];

  for(let i=0; i < jpegArea; i++) {
    jpegPixels.push(jpegPixelColor[0]);
    jpegPixels.push(jpegPixelColor[1]);
    jpegPixels.push(jpegPixelColor[2]);
    jpegPixels.push(1); // alpha channel value - ignored in JPEGs
  }

  // Define the structure of the image
  const jpegImage: Image = {
    width: jpegWidth,
    height: jpegHeight,
    data: new Uint8Array(jpegPixels)
  }

  // Encode image data to jpeg format
  const jpegRaw: Image = pixelsToBin(jpegImage, 100); //Quality 100 (default is 50)

  // Save the binary image to the file system
  await Deno.writeFile(`${fileName}.jpg`, jpegRaw.data);
}

// Save the jpeg image with specified dimensions, color, and name
await saveJpeg(640, 360, "#f44336", "red-500");
await saveJpeg(640, 360, "#4caf50", "green-500");
await saveJpeg(640, 360, "#2196f3", "blue-500");

Given the capabilities of this library, achieving embedding image dimensions onto the image itself would involve implementing additional logic within the code.

Answer №1

Great news! I conducted some thorough research and managed to utilize a different library to accomplish the task successfully.

Below are the outcomes:

// Utilizing Canvas API for Deno, which is adapted from canvaskit-wasm (Skia)
import { createCanvas } from "https://deno.land/x/canvas/mod.ts";
// Incorporating std base64 encoder
import { encode as binTob64 } from "https://deno.land/std/encoding/base64.ts";

// Generating a PNG Image in Deno using the Canvas API

/***
 * 
 * Material Design Colors
 * 
 * 19 background colors in the 500 range
 * 
 * RED ---------> #f44336 -> rgba(244,  67,  54, 1)
 * PINK --------> #e91e63 -> rgba(233,  30,  99, 1)
 * PURPLE ------> #9c27b0 -> rgba(156,  39, 176, 1)
 * DEEP-PURPLE -> #673ab7 -> rgba(103,  58, 183, 1)
 * INDIGO ------> #3f51b5 -> rgba( 63,  81, 181, 1)
 * BLUE --------> #2196f3 -> rgba( 63,  81, 181, 1)
 * LIGHT-BLUE --> #03a9f4 -> rgba(  3, 169, 244, 1)
 * CYAN --------> #00bcd4 -> rgba(  0, 188, 212, 1)
 * TEAL --------> #009688 -> rgba(  0, 150, 136, 1)
 * GREEN -------> #4caf50 -> rgba( 76, 175,  80, 1)
 * LIGHT-GREEN -> #8bc34a -> rgba(139, 195,  74, 1)
 * LIME --------> #cddc39 -> rgba(205, 220,  57, 1)
 * YELLOW ------> #ffeb3b -> rgba(255, 235,  59, 1)
 * AMBER -------> #ffc107 -> rgba(255, 193,   7, 1)
 * ORANGE ------> #ff9800 -> rgba(255, 152,   0, 1)
 * DEEP-ORANGE -> #ff5722 -> rgba(255,  87,  34, 1)
 * BROWN -------> #795548 -> rgba(121,  85,  72, 1)
 * GREY --------> #9e9e9e -> rgba(158, 158, 158, 1)
 * BLUE-GREY ---> #607d8b -> rgba( 96, 125, 139, 1)
 * 
 */

async function savePNG(w: number, h: number, bgHex: string, fgHex: string, fileName: string) {

  const pngWidth: number = w;
  const pngHeight: number = h;

  const canvas = createCanvas(pngWidth, pngHeight);

  const canvasCenter: any = {
    x: Math.floor(canvas.width / 2),
    y: Math.floor(canvas.height / 2),
  };

  const context2d = canvas.getContext("2d");

  context2d.fillStyle = bgHex;
  context2d.fillRect(0, 0, pngWidth, pngHeight); // X, Y, W, H

  // Setting font sizes: 12, 18, 24, 36, 48, 60, 72
  const fontSize: number = Math.floor(pngHeight * 0.125); // Defined as % of pngHeight

  context2d.fillStyle = fgHex;
  context2d.font = `${fontSize}px sans-serif`;

  // Text to be displayed
  const text: string = `${pngWidth}x${pngHeight}`;

  const charWidth: number = Math.floor(context2d.measureText("X").width);
  const textLength: number = text.length;
  const textWidth: number = charWidth * textLength;

  const textHeight: number = parseInt(context2d.font);

  context2d.fillText(text, Math.floor(canvasCenter.x - (textWidth / 2)), Math.floor(canvasCenter.y + (textHeight / 2))); // X, Y position

  await Deno.writeFile(`${fileName}.png`, canvas.toBuffer());

}

// Save the jpeg file with dimensions, colors & file name
await savePNG(1000, 1000, "#f44336", "#ffcdd2", "red-500");

// Save the jpeg file with dimensions, colors & file name
await savePNG(750, 750, "#4caf50", "#c8e6c9", "green-500");

// Save the jpeg file with dimensions, colors & file name
await savePNG(50, 50, "#2196f3", "#bbdefb", "blue-500");

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Instead of leaving an Enum value as undefined, using a NONE value provides a more explicit

I've noticed this pattern in code a few times and it's got me thinking. When you check for undefined in a typescript enum, it can lead to unexpected behavior like the example below. enum DoSomething { VALUE1, VALUE2, VALUE3, } f ...

Is there a way to convert a literal type from the type level to the term level in TypeScript?

Consider this scenario: I have created a type that can only hold one specific value: export type IfEqual<T, U> = (<G>() => G extends T ? 1 : 2) extends ...

Creating a cohesive "notification" feature in React with TypeScript that is integrated with one specific component

I am currently working on designing my application to streamline all notifications through a single "snackbar" style component (utilizing the material UI snackbar component) that encompasses the entire app: For instance class App extends React.Component ...

The type 'MenuOptions[]' cannot be assigned to type 'empty[]'

Even after numerous attempts, I am still grappling with TypeScript problems. Currently, I am at a loss on how to resolve this particular issue, despite all the research I have conducted. The code snippet below is what I am working with, but I am struggling ...

Exported data in Vue3 components cannot be accessed inside the component itself

Essentially, I'm working on creating a dynamic array in Vue3. Each time a button is clicked, the length of the array should increase. Below is the code snippet. <div class="package-item" v-for="n in arraySize"></div> e ...

A guide to efficiently removing an element in Angular using TypeScript by considering certain properties

I need help removing an element from an array based on any property such as its key, name, or email. HTML <tr *ngFor="let person of persons;" (click)="remove(person.key)"> <td>{{person.key}}</td> <td>{{person.name}}</td> ...

What is the correct way to format React's dispatch function in order to utilize a "then" method similar to a Promise?

I'm working on a simple app that dispatches an action upon first load to populate the store. However, I'm facing an issue with trying to run a then method on dispatch, as typescript is throwing errors. (As per redux's documentation, the ret ...

Dealing with null-safe operators issues has been a challenge for me, especially while working on my Mac using

Hey everyone! I'm encountering errors when using null sage operators in TypeScript. Can someone help me figure out how to solve this issue? By the way, I'm working on Visual Studio Code for Mac. https://i.stack.imgur.com/huCns.png ...

Implement type declarations for a React JS form validation schema

I encountered the following scenario: interface FORM<P> { onSubmit: (d: P) => void; schema?: yup.SchemaOf<P>; } This is an example of my onSubmit function: const onSubmit = (d: { firstName: string; lastName: string }) => { conso ...

Custom options titled MUI Palette - The property 'primary' is not found in the 'TypeBackground' type

I am currently working on expanding the MUI palette to include my own custom properties. Here is the code I have been using: declare module '@mui/material/styles' { interface Palette { border: Palette['primary'] background: Pa ...

The error message indicates that the 'aboutData' property is not found within the 'never[]' data type

What is the correct method for printing array elements without encountering the error message "Property 'post_title' does not exist on type 'never[]'?" How can interfaces be used to define variables and utilize them in code for both ab ...

Guide on navigating to a specific page with ngx-bootstrap pagination

Is there a way to navigate to a specific page using ngx-bootstrap pagination by entering the page number into an input field? Check out this code snippet: ***Template:*** <div class="row"> <div class="col-xs-12 col-12"> ...

Since updating from Angular 16 to 17, I am experiencing a TypeScript compilation issue specifically related to 'openui5'

Everything was running smoothly in Angular16. I had "@types/openui5" : "1.40.4" listed in my dev-dependencies. Here is how it's configured in the tsconfig.json: { "compilerOptions": { "downlevelIteration": ...

Throw TypeError: The `pipe` property of `ngrx/store` is undefined during testing

Here is the code snippet from my TypeScript file: this.store.pipe(select(subscribe.getRegCategories)).pipe(takeUntil(this.ngUnsubscribe)).subscribe(data => { if (data && data.length) { this.allRegCategories = data; ...

Explain the object type that is returned when a function accepts either an array of object keys or an object filled with string values

I've written a function called getParameters that can take either an array of parameter names or an object as input. The purpose of this function is to fetch parameter values based on the provided parameter names and return them in a key-value object ...

How can I retrieve query parameters in the Server app directory of Next.js 13 using a function?

I am looking to retrieve a query token from localhost/get/point?token=hello. import { NextResponse } from 'next/server' import base64url from 'base64url' type Params = { token: string } export async function GET(req: Request, contex ...

Leveraging React Native to position a view absolutely in the center of the screen without obstructing any other components

How can I center an image inside a view in the middle of the screen using position: "absolute"? The issue is that the view takes up 100% of the width and height of the screen, causing all components underneath it (such as input fields and buttons ...

What is the method for determining the type of search results returned by Algolia?

My connection between firestore and algoliasearch is working well. I am implementing it with the help of typescript in nextjs. I am attempting to fetch the results using the following code snippet products = index.search(name).then(({hits}) => { ret ...

Angular II slash avoiding Pipe

I am working on developing a customized pipe in Angular 2 that will handle the replacement of the backslash ('\') character in a given string. This backslash is commonly used to escape special characters. What I have accomplished so far: T ...

Are the missing attributes the type of properties that are absent?

I have a pair of interfaces: meal-component.ts: export interface MealComponent { componentId: string; componentQuantity: number; } meal.ts: import { MealComponent } from 'src/app/interfaces/meal-component'; export interface Meal { ...