glennwilton / jsColorEngine

jsColorEngine is a color management engine using ICC profiles in 100% JavaScript.
Other
22 stars 2 forks source link

Trying to convert image to CMYK and display it - rgbData is not a valid imageData format #1

Open iroth opened 8 months ago

iroth commented 8 months ago

I am trying to follow your code example for coverting an image to CMYK. This part is failing: var rgbData = cmyk2rgb.transformArray(cmykData, false, true);
ctx.putImageData(rgbData, 0, 0);

where I get an exception for putImageData parameter 1 is not of type ImageData.

I tried to fix the code by putting the result rgbData as the data field of a synthetic imageData object, but all my attempts failed. What am I missing?

glennwilton commented 8 months ago

Hey the example is not complete, I will update it, you need to use createImageData to create a new object to to accept the converted colours

image.onload = function() { var canvas = document.createElement('canvas'); canvas.width = image.width; canvas.height = image.height; var ctx = canvas.getContext('2d'); ctx.drawImage(image, 0, 0); var imageData = ctx.getImageData(0, 0, image.width, image.height);

// convert RGB to RGB with soft proofing
var data = proofTransform.transformArray(imageData.data, true, true, true);

// Create a new ImageData object with the modified pixel data
var newImageData = ctx.createImageData(image.width, image.height);
newImageData.data.set(data);

ctx.putImageData(newImageData, 0, 0);
document.body.appendChild(canvas);

}

iroth commented 8 months ago

Thanks a lot for the quick response! now the code is working, however, both images in the following example - based on your image to cmyk snippet with the fixes - look the same (original loaded, and the rgb->cmyk->rgb).

EDIT: I had a bug in my code which caused the clamped array not to be manipulated at all, I now edited the code, but now it seems the conversion creates a white image...

I had to go through format conversion since by default the transform array returns a float array.

      var cmykData = rgb2cmyk.transformArray(imageData.data, true, false);
      // convert from CMYK to RGB
      let dataArray = cmyk2rgb.transformArray(
        cmykData,
        false,
        true,
        false, // this is needed as the original does not have alpha
        imageData.width * imageData.height,
        "int8"
      );
      let clamped = new Uint8ClampedArray(imageData.data);
      let dataArrayIndex = 0;
      for (let i = 0; i < imageData.data.length; i++) {
        if (i % 4 === 3) {
          clamped[i] = 255;
        } else {
          clamped[i] = dataArray[dataArrayIndex++];
        }
      }
      var rgbData = ctx.createImageData(image.width, image.height);
      rgbData.data.set(clamped);
      ctx.putImageData(rgbData, 0, 0);

Note: if this makes a difference, I am using the JapanColor2002Newspaper.icc profile for the cmyk coversion

glennwilton commented 8 months ago

Hi

A few things

  1. Your cmyk2rgb transform should be relative-Colorimetric; if you choose perceptuall it may re-expand the colours to a wider gamut in RGB. If you want to simulate the paper colour choose absolute Colorimetric.

  2. If you are just soft proofing and you don't care about the CMYK values, you can create a multi-stage transform proofTransform.createMultiStage(['*srgb', eIntent.perceptual, CMYKprofile, eIntent.relative, '*srgb']); see the example in the MD

  3. My default the result of the transform will be array or object, new Transform({dataFormat: 'int8'}); will cause all output to be 0-255 and setting precession to 0 will round for you.

  4. If you want speed at the expense of a little accuracy, pre-build a LUT with the transform, but keep the transform in memory as you don't want to constantly be pre-building the same lut new Transform({ buildLUT: true})

  5. The transformArray can take care of all the conversions for you, including handing the alpha, So you should no need to be copying to a clamped Array, set the output format to int8, AND set your transform to dataFormat : 'int8' and it will create a Uint8ClampedArray, Look at the function transformArray on line 923 in the Transform.js module,

  6. Test your transform on a single pixel. For example, transform the array CMYK [0,0,0,0] should come out as [255,255,255] or close. and the same with black [255,255,255,255] would be something like [32,32,32] as CMYK black is not a pure rgb black, Again with a red [0,255,255,0] would be an RGB red, you can easily just console.log those

If you're still having issues post your createTransform code and options as well.

iroth commented 8 months ago

Thanks again, but still no luck. I failed to mention that I am trying this with the web in-browser file (jsColorEngineWeb.js). I now created a proofTransform as proposed, but still I am getting float rather than int8. I placed all my code in this repo, so maybe this can help understand the issue: https://github.com/iroth/color-test I am probably missing something basic, as I am not so experienced with canvas and image manipulation in JS.

iroth commented 8 months ago

one small note: in the docs/readme you use {buildLUT: true}, but the code seems to expect builtLut as an option.