#pragma once
// STPic.h: interface for the CSTPic class.
//
//////////////////////////////////////////////////////////////////////
#include <assert.h>
#ifndef STPIC_H
#define STPIC_H
/****** SASA Picture Class Template **********************
Currently there are two types of this object
ST_RGBM - CSTPic<UC_PIXEL> - UCHAR r,g,b,m channels
ST_RGBM64 - CSTPic<US_PIXEL> - USHORT r,g,b,m channels
The CMAP RASTER pictures have to be converted to ST_RGBM or
ST_RGBM64, but the CMAP information can be used with the help
of m_raster.
m_lX,m_lY - the length of the picture in X,Y direction
m_pic - the buffer of the picture
m_ras - stores the pointer to the original RASTER picture
************************************************************/
#include "toonz4.6/udit.h"
#include "toonz4.6/raster.h"
#include "toonz4.6/pixel.h"
#include "SDef.h"
#include "SError.h"
#include "timagecache.h"
#include "trasterimage.h"
#define ISINPIC(x, y) (m_pic && x >= 0 && x < m_lX && y >= 0 && y < m_lY)
typedef enum {
ST_NIL, // EMPTY
ST_RGBM, // UC_PIXEL
ST_RGBM64 // US_PIXEL
} ST_TYPE;
//! Old 4.6 picture class template (sandor fxs). There is a significant
//! modification
//! to be considered starting from Toonz 6.1 - the allocated raster is now
//! managed
//! by the image cache. Basically, when constructing one such object, the image
//! is locked
//! in the Toonz cache - and you must remember to unlock and relock it along
//! inactivity periods.
template <class P>
class CSTPic {
std::string m_cacheId;
TRasterImageP m_picP;
public:
int m_lX, m_lY;
P *m_pic;
const RASTER *m_ras;
CSTPic(void)
: m_cacheId(TImageCache::instance()->getUniqueId())
, m_lX(0)
, m_lY(0)
, m_pic(0)
, m_ras(0) {}
void nullPic() {
// if ( m_pic ) { delete [] m_pic; m_pic=0; }
unlock();
TImageCache::instance()->remove(m_cacheId);
}
//! Retrieves the raster image from the cache to work with it.
void lock() {
m_picP = TImageCache::instance()->get(m_cacheId, true);
m_pic = (P *)m_picP->getRaster()->getRawData();
}
//! Release the raster image for inactivity periods.
void unlock() {
m_picP = 0;
m_pic = 0;
}
CSTPic(const int lX, const int lY)
: // throw(SMemAllocError) :
m_cacheId(TImageCache::instance()->getUniqueId())
, m_lX(lX)
, m_lY(lY)
, m_pic(0)
, m_ras(0) {
try {
initPic();
} catch (SMemAllocError) {
null();
throw;
}
}
CSTPic(const CSTPic<P> &sp)
: // throw(SMemAllocError) :
m_lX(sp.m_lX)
, m_lY(sp.m_lY)
, m_pic(0)
, m_ras(sp.m_ras) {
try {
initPic();
copyPic(sp);
} catch (SMemAllocError) {
null();
throw;
}
}
~CSTPic(void) { null(); }
//! Allocates the specified raster and surrenders it to the image cache.
//!\b NOTE: The raster being initialized is LOCKED after this call.
//! You must remember to UNLOCK it when no more used.
void initPic() // throw(SMemAllocError)
{
nullPic();
if (m_lX > 0 && m_lY > 0) {
// m_pic=new P[m_lX*m_lY];
TRasterGR8P ras(m_lX * m_lY * sizeof(P), 1);
if (!ras) throw SMemAllocError("in initPic");
TImageCache::instance()->add(m_cacheId, TRasterImageP(ras));
lock();
} else {
char s[200];
snprintf(s, sizeof(s), "in initPic lXY=(%d,%d)\n", m_lX, m_lY);
throw SMemAllocError(s);
}
}
void null() {
nullPic();
m_lX = m_lY = 0;
m_ras = 0;
}
// Draws the border of the CSTPic
void drawRect(const P &ip) {
SRECT rect = {0, 0, m_lX - 1, m_lY - 1};
drawRect(rect, ip);
}
//------------------- The following need to be *LOCKED* before the call
//------------------
// Currently there are two types of STPic
// ST_RGBM - UCHAR r,g,b,m channels
// ST_RGBM64 - USHORT r,g,b,m channels
ST_TYPE getType() const {
if (!m_pic) return ST_NIL;
if (sizeof(m_pic->r) == sizeof(UCHAR)) return ST_RGBM;
if (sizeof(m_pic->r) == sizeof(USHORT)) return ST_RGBM64;
return ST_NIL;
}
// Copies the 'sp' CSTPic into 'm_pic'
void copyPic(const CSTPic<P> &sp) {
P p;
for (int y = 0; y < m_lY && y < sp.m_lY; y++)
for (int x = 0; x < m_lX && x < sp.m_lX; x++) {
sp.getPixel(x, y, p);
setRGBM(x, y, p);
}
}
// Copies the 'r' rectangle of 'sp' CSTPic into the 'p' position
// of 'm_pic'
void copyPic(const CSTPic<P> &sp, const SRECT &r, const SPOINT &p) {
P ip;
int xs, ys, xd, yd;
for (ys = r.y0, yd = p.y; ys <= r.y1; ys++, yd++)
for (xs = r.x0, xd = p.x; xs <= r.x1; xs++, xd++) {
sp.getPixel(xs, ys, ip);
setRGBM(xd, yd, ip);
}
}
// Draws a rectangle into CSTPic
void drawRect(const SRECT &rect, const P &ip) {
for (int x = rect.x0; x <= rect.x1; x++) {
setRGBM(x, rect.y0, ip);
setRGBM(x, rect.y1, ip);
}
for (int y = rect.y0; y <= rect.y1; y++) {
setRGBM(rect.x0, y, ip);
setRGBM(rect.x1, y, ip);
}
}
void erease() {
P p = {0, 0, 0, 0};
fill(p);
}
void fill(const P &ip) {
for (int y = 0; y < m_lY; y++)
for (int x = 0; x < m_lX; x++) setRGBM(x, y, ip);
}
// Checks whether (x,y) is a proper coordinate
inline bool isInPic(const int x, const int y) const {
return (m_pic && x >= 0 && x < m_lX && y >= 0 && y < m_lY);
}
// Sets the color of the CSTPic pixel
void setRGB(const int x, const int y, const P &ip) {
if (ISINPIC(x, y)) {
P *p = m_pic + y * m_lX + x;
p->r = ip.r;
p->g = ip.g;
p->b = ip.b;
}
}
void setRGBM(const int x, const int y, const P &ip) {
if (ISINPIC(x, y)) {
P *p = m_pic + y * m_lX + x;
p->r = ip.r;
p->g = ip.g;
p->b = ip.b;
p->m = ip.m;
}
}
// Sets the color of the CSTPic pixel
void setRGB(const int x, const int y, const I_PIXEL &ip) {
if (ISINPIC(x, y)) {
P *p = m_pic + y * m_lX + x;
if (getType() == ST_RGBM) {
p->r = (UCHAR)I_CUT_0_255(ip.r);
p->g = (UCHAR)I_CUT_0_255(ip.g);
p->b = (UCHAR)I_CUT_0_255(ip.b);
} else if (getType() == ST_RGBM64) {
p->r = (USHORT)I_CUT_0_65535(ip.r);
p->g = (USHORT)I_CUT_0_65535(ip.g);
p->b = (USHORT)I_CUT_0_65535(ip.b);
}
}
}
void setRGBM(const int x, const int y, const I_PIXEL &ip) {
if (ISINPIC(x, y)) {
P *p = m_pic + y * m_lX + x;
if (getType() == ST_RGBM) {
p->r = (UCHAR)I_CUT_0_255(ip.r);
p->g = (UCHAR)I_CUT_0_255(ip.g);
p->b = (UCHAR)I_CUT_0_255(ip.b);
p->m = (UCHAR)I_CUT_0_255(ip.m);
} else if (getType() == ST_RGBM64) {
p->r = I_CUT_0_65535(ip.r);
p->g = I_CUT_0_65535(ip.g);
p->b = I_CUT_0_65535(ip.b);
p->m = I_CUT_0_65535(ip.m);
}
}
}
// Gets the color of the CSTPic pixel
void getPixel(const int x, const int y, P &ip) const {
if (ISINPIC(x, y)) {
const P *p = m_pic + y * m_lX + x;
ASSIGN_PIXEL(&ip, p);
return;
}
ip.r = ip.g = ip.b = ip.m = 0;
}
void getPixel(const int x, const int y, I_PIXEL &ip) const {
if (ISINPIC(x, y)) {
const P *p = m_pic + y * m_lX + x;
ip.r = (int)p->r;
ip.g = (int)p->g;
ip.b = (int)p->b;
ip.m = (int)p->m;
return;
}
ip.r = ip.g = ip.b = ip.m = 0;
}
// Gets the color of a RASTER pixel
// RASTER operation always gets I_PIXEL color and I_PIXEL is casted
// to the right type
void getRasterPixel(const RASTER *ras, const int x, const int y,
I_PIXEL &ip) const {
if (x >= 0 && x < ras->lx && y >= 0 && y < ras->ly && ras->buffer) {
LPIXEL *pL;
LPIXEL pLL;
SPIXEL *pS;
UD44_CMAPINDEX32 *ci32;
// UD44_PIXEL32* pen;
UD44_PIXEL32 *col;
switch (ras->type) {
case (RAS_RGBM):
pL = (LPIXEL *)(ras->buffer) + y * ras->wrap + x;
ip.r = (int)pL->r;
ip.g = (int)pL->g;
ip.b = (int)pL->b;
ip.m = (int)pL->m;
break;
case (RAS_RGBM64):
pS = (SPIXEL *)(ras->buffer) + y * ras->wrap + x;
ip.r = (int)pS->r;
ip.g = (int)pS->g;
ip.b = (int)pS->b;
ip.m = (int)pS->m;
break;
case (RAS_CM32):
ci32 = (UD44_CMAPINDEX32 *)(ras->buffer) + y * ras->wrap + x;
col = (UD44_PIXEL32 *)(ras->cmap.buffer);
PIX_CM32_MAP_TO_RGBM(*ci32, col, pLL)
ip.r = (int)pLL.r;
ip.g = (int)pLL.g;
ip.b = (int)pLL.b;
ip.m = (int)pLL.m;
break;
default:
break;
}
} else
ip.r = ip.g = ip.b = ip.m = 0;
}
void getRasterPixel(const int x, const int y, I_PIXEL &ip) const {
if (m_ras)
getRasterPixel(m_ras, x, y, ip);
else
ip.r = ip.g = ip.b = ip.m = 0;
}
// Sets the color of a RASTER pixel
// RASTER operation always gets I_PIXEL color and I_PIXEL is casted
// to the right type
void setRasterPixel(RASTER *ras, const int x, const int y,
const I_PIXEL &ip) const {
if (x >= 0 && x < ras->lx && y >= 0 && y < ras->ly && ras->buffer) {
LPIXEL *pL;
SPIXEL *pS;
switch (ras->type) {
case (RAS_RGBM):
pL = (LPIXEL *)(ras->buffer) + y * ras->wrap + x;
pL->r = (UCHAR)ip.r;
pL->g = (UCHAR)ip.g;
pL->b = (UCHAR)ip.b;
pL->m = (UCHAR)ip.m;
break;
case (RAS_RGBM64):
pS = (SPIXEL *)(ras->buffer) + y * ras->wrap + x;
pS->r = (USHORT)ip.r;
pS->g = (USHORT)ip.g;
pS->b = (USHORT)ip.b;
pS->m = (USHORT)ip.m;
break;
default:
break;
}
}
}
bool copy_raster(const RASTER *ir, RASTER *out_r, const int xBeg,
const int yBeg, const int xEnd, const int yEnd, const int ox,
const int oy)
{
if ((ir->lx <= 0) || (ir->ly <= 0) || (out_r->lx <= 0) || (out_r->ly <= 0))
return false;
if (ir->buffer == NULL || out_r->buffer == NULL) return false;
if (out_r->type == RAS_CM32) return false;
if (ir->type == RAS_CM32 && (ir->cmap.buffer == NULL)) return false;
for (int y = yBeg, yy = oy; y <= yEnd; y++, yy++)
for (int x = xBeg, xx = ox; x <= xEnd; x++, xx++) {
I_PIXEL ip;
getRasterPixel(ir, x, y, ip);
if (xx >= 0 && yy >= 0 && xx < out_r->lx && yy < out_r->ly) {
LPIXEL *pL;
SPIXEL *pS;
if (out_r->type == RAS_RGBM &&
(ir->type == RAS_RGBM || ir->type == RAS_CM32)) {
pL = (LPIXEL *)(out_r->buffer) + yy * out_r->wrap + xx;
pL->r = (UCHAR)ip.r;
pL->g = (UCHAR)ip.g;
pL->b = (UCHAR)ip.b;
pL->m = (UCHAR)ip.m;
} else if (out_r->type == RAS_RGBM && ir->type == RAS_RGBM64) {
pL = (LPIXEL *)(out_r->buffer) + yy * out_r->wrap + xx;
pL->r = PIX_BYTE_FROM_USHORT((USHORT)ip.r);
pL->g = PIX_BYTE_FROM_USHORT((USHORT)ip.g);
pL->b = PIX_BYTE_FROM_USHORT((USHORT)ip.b);
pL->m = PIX_BYTE_FROM_USHORT((USHORT)ip.m);
} else if (out_r->type == RAS_RGBM64 &&
(ir->type == RAS_RGBM || ir->type == RAS_CM32)) {
pS = (SPIXEL *)(out_r->buffer) + yy * out_r->wrap + xx;
pS->r = PIX_USHORT_FROM_BYTE((UCHAR)ip.r);
pS->g = PIX_USHORT_FROM_BYTE((UCHAR)ip.g);
pS->b = PIX_USHORT_FROM_BYTE((UCHAR)ip.b);
pS->m = PIX_USHORT_FROM_BYTE((UCHAR)ip.m);
} else if (out_r->type == RAS_RGBM64 && ir->type == RAS_RGBM64) {
pS = (SPIXEL *)(out_r->buffer) + yy * out_r->wrap + xx;
pS->r = (USHORT)ip.r;
pS->g = (USHORT)ip.g;
pS->b = (USHORT)ip.b;
pS->m = (USHORT)ip.m;
}
}
}
return true;
}
// Generates and reads the CSTPic using RASTER.
// \b NOTE: This LOCKS the raster being read. You must remember to unlock it
// afterwards
// when it is needed no more.
virtual void read(const RASTER *ras) // throw(SMemAllocError)
{
try {
null();
if (((ras->type == RAS_RGBM || ras->type == RAS_RGBM64) && ras->buffer &&
ras->lx > 0 && ras->ly > 0) ||
(ras->type == RAS_CM32 && ras->buffer && ras->cmap.buffer &&
ras->lx > 0 && ras->ly > 0)) {
m_lX = ras->lx;
m_lY = ras->ly;
m_ras = ras;
initPic();
lock();
ST_TYPE type = getType();
P *p = m_pic;
I_PIXEL ip;
memset(&ip, 0, sizeof(I_PIXEL));
for (int y = 0; y < m_lY; y++)
for (int x = 0; x < m_lX; x++, p++) {
getRasterPixel(ras, x, y, ip);
switch (type) {
case (ST_RGBM):
if (ras->type == RAS_RGBM64) {
// RGBM have to be 'scaled' from USHORT to UCHAR
p->r = PIX_BYTE_FROM_USHORT((USHORT)ip.r);
p->g = PIX_BYTE_FROM_USHORT((USHORT)ip.g);
p->b = PIX_BYTE_FROM_USHORT((USHORT)ip.b);
p->m = PIX_BYTE_FROM_USHORT((USHORT)ip.m);
} else {
// RGBM are UCHAR in the raster
p->r = (UCHAR)ip.r;
p->g = (UCHAR)ip.g;
p->b = (UCHAR)ip.b;
p->m = (UCHAR)ip.m;
}
break;
case (ST_RGBM64):
if (ras->type == RAS_RGBM64) {
// RGBM are USHORT in the raster
p->r = ip.r;
p->g = ip.g;
p->b = ip.b;
p->m = ip.m;
} else {
// RGBM have to be 'scaled' from UCHAR to USHORT
p->r = PIX_USHORT_FROM_BYTE((UCHAR)ip.r);
p->g = PIX_USHORT_FROM_BYTE((UCHAR)ip.g);
p->b = PIX_USHORT_FROM_BYTE((UCHAR)ip.b);
p->m = PIX_USHORT_FROM_BYTE((UCHAR)ip.m);
}
break;
default:
break;
}
}
}
} catch (SMemAllocError) {
null();
throw;
}
}
const CSTPic<P> &operator=(const CSTPic<P> &sp) // throw(SMemAllocError)
{
try {
null();
m_lX = sp.m_lX;
m_lY = sp.m_lY;
m_ras = sp.m_ras;
initPic();
copyPic(sp);
} catch (SMemAllocError) {
null();
throw;
}
return (*this);
}
// Writes the CSTPic into the RASTER
virtual void write(RASTER *ras) const // throw(SWriteRasterError)
{
if ((ras->type == RAS_RGBM || ras->type == RAS_RGBM64) && ras->lx > 0 &&
ras->ly > 0 && ras->buffer) {
int x, y;
I_PIXEL ip;
const P *p;
for (y = 0; y < m_lY && y < ras->ly; y++)
for (x = 0; x < m_lX && x < ras->lx; x++) {
p = m_pic + y * m_lX + x;
ip.r = (int)p->r;
ip.g = (int)p->g;
ip.b = (int)p->b;
ip.m = (int)p->m;
setRasterPixel(ras, x, y, ip);
}
} else
throw SWriteRasterError("(bad Raster type)");
}
// Writes the 'r' rectangle of CSTPic into the 'p' position in RASTER
virtual void write(RASTER *ras, const SRECT &r, const SPOINT &p) const
// throw(SWriteRasterError)
{
if (ras->type == RAS_RGBM || ras->type == RAS_RGBM64) {
int xs, ys, xd, yd;
P pp;
I_PIXEL ip;
for (ys = r.y0, yd = p.y; ys <= r.y1; ys++, yd++)
for (xs = r.x0, xd = p.x; xs <= r.x1; xs++, xd++) {
getPixel(xs, ys, pp);
ip.r = (int)pp.r;
ip.g = (int)pp.g;
ip.b = (int)pp.b;
ip.m = (int)pp.m;
setRasterPixel(ras, xd, yd, ip);
}
} else
throw SWriteRasterError("(bad Raster type)");
}
virtual void writeOutBorder(const RASTER *rasin, const int border,
RASTER *ras, const SRECT &r,
const SPOINT &p) const
// throw(SWriteRasterError)
{
assert(rasin->type == RAS_CM32);
UD44_PIXEL32 *col = (UD44_PIXEL32 *)(rasin->cmap.buffer);
if (ras->type == RAS_RGBM || ras->type == RAS_RGBM64) {
int xs, ys, xd, yd;
I_PIXEL ip;
for (ys = r.y0, yd = p.y; ys <= r.y1; ys++, yd++)
for (xs = r.x0, xd = p.x; xs <= r.x1; xs++, xd++) {
int x = xd - border;
int y = yd - border;
if (x >= 0 && y >= 0 && x < rasin->lx && y < rasin->ly) {
UD44_CMAPINDEX32 pixel =
*(((UD44_CMAPINDEX32 *)rasin->buffer) + y * rasin->wrap + x);
// int tone = (pix&0xff);
// int paint = ((pix>>8)&0xfff);
if ((pixel & 0xff) == 0 || ((pixel >> 8) & 0xfff) != 0) {
LPIXEL pLL;
PIX_CM32_MAP_TO_RGBM(pixel, col, pLL)
ip.r = (int)pLL.r;
ip.g = (int)pLL.g;
ip.b = (int)pLL.b;
ip.m = (int)pLL.m;
setRasterPixel(ras, xd, yd, ip);
continue;
}
}
P pp;
getPixel(xs, ys, pp);
ip.r = (int)pp.r;
ip.g = (int)pp.g;
ip.b = (int)pp.b;
ip.m = (int)pp.m;
setRasterPixel(ras, xd, yd, ip);
}
} else
throw SWriteRasterError("(bad Raster type)");
}
void convertToCurrentType(P &d, const I_PIXEL &s) const {
if (getType() == ST_RGBM) {
d.r = (UCHAR)s.r;
d.g = (UCHAR)s.g;
d.b = (UCHAR)s.b;
d.m = (UCHAR)s.m;
}
if (getType() == ST_RGBM64) {
d.r = (USHORT)s.r;
d.g = (USHORT)s.g;
d.b = (USHORT)s.b;
d.m = (USHORT)s.m;
}
}
bool isSameColor(const P *a, const P *b) const {
if (a->r == b->r && a->g == b->g && a->b == b->b) return true;
return false;
}
void colorNoise(const I_PIXEL &cc1, const I_PIXEL &cc2, const double d) {
P c1, c2;
if (d <= 0) return;
convertToCurrentType(c1, cc1);
convertToCurrentType(c2, cc2);
int xy = m_lX * m_lY;
P *pPic = m_pic;
for (int i = 0; i < xy; i++, pPic++)
if (isSameColor(&c1, pPic)) {
double q = (rand() % 101) / 100.0;
q = d * q;
q = q > 1.0 ? 1.0 : q;
double r = (1.0 - q) * (double)c1.r + q * (double)c2.r;
double g = (1.0 - q) * (double)c1.g + q * (double)c2.g;
double b = (1.0 - q) * (double)c1.b + q * (double)c2.b;
r = D_CUT_0_255(r);
g = D_CUT_0_255(g);
b = D_CUT_0_255(b);
pPic->r = UC_ROUND(r);
pPic->g = UC_ROUND(g);
pPic->b = UC_ROUND(b);
}
}
/*
void hlsNoise(const double d)
{ int xy=m_lX*m_lY;
P* p=m_pic;
for( int i=0; i<xy; i++,p++ )
if ( p->m>0 ) {
double h,l,s,q;
rgb2hls(p->r,p->g,p->b,&h,&l,&s);
q=1.0-d*(double)((rand()%201)-100)/100.0;
l*=q;
hls2rgb(h,l,s,&(p->r),&(p->g),&(p->b));
}
}
*/
void rgb2hls(UCHAR r, UCHAR g, UCHAR b, double *h, double *l, double *s) {
double ma, mi, delta, sum;
double rf, gf, bf;
rf = (double)r / 255.0;
gf = (double)g / 255.0;
bf = (double)b / 255.0;
ma = rf > gf ? rf : gf;
ma = ma > bf ? ma : bf;
mi = rf < gf ? rf : gf;
mi = mi < bf ? mi : bf;
sum = ma + mi;
delta = ma - mi;
*l = sum / 2.0;
if (fabs(delta) < 0.000001) {
*s = 0.0;
*h = UNDEFINED;
} else {
*s = *l <= 0.5 ? delta / sum : delta / (2.0 - sum);
*h = fabs((rf - ma)) < 0.000001
? (gf - bf) / delta
: (fabs((gf - ma)) < 0.000001 ? 2.0 + (bf - rf) / delta
: 4.0 + (rf - gf) / delta);
*h *= 60;
*h = *h < 0.0 ? *h + 360.0 : *h;
}
}
double fromHue(double n1, double n2, double hue) {
double v, h;
h = hue > 360.0 ? hue - 360.0 : hue;
h = h < 0.0 ? h + 360.0 : h;
if (h < 60.0) {
v = n1 + (n2 - n1) * h / 60.0;
} else if (h < 180.0) {
v = n2;
} else if (h < 240.0) {
v = n1 + (n2 - n1) * (240.0 - h) / 60.0;
} else
v = n1;
return (v);
}
void hls2rgb(double h, double l, double s, UCHAR *r, UCHAR *g, UCHAR *b) {
double rf, gf, bf;
double m1, m2;
m2 = l <= 0.5 ? l * (1.0 + s) : l + s - l * s;
m1 = 2 * l - m2;
if (fabs(s - 0.0) < 0.000001) {
if (fabs(h - UNDEFINED) < 0.000001) {
rf = gf = bf = l * 255.0;
} else
rf = gf = bf = 0.0;
} else {
rf = fromHue(m1, m2, h + 120.0) * 255.0;
gf = fromHue(m1, m2, h) * 255.0;
bf = fromHue(m1, m2, h - 120.0) * 255.0;
}
rf = D_CUT_0_255(rf);
gf = D_CUT_0_255(gf);
bf = D_CUT_0_255(bf);
*r = UC_ROUND(rf);
*g = UC_ROUND(gf);
*b = UC_ROUND(bf);
}
};
#endif // !defined(AFX_STPIC_H__BABE9488_F054_11D5_B927_0040F674BE6A__INCLUDED_)